var:局部变量类型推断
学完你要能自己回答
- var 是 JDK 11 的新特性吗?它推断出的类型在什么时候确定?
- 哪些位置不能用 var?
var x = null与var a = {1, 2}为什么编译失败? - lambda 参数用 var 有什么额外能力?为什么不能写成
(var x, y)?
版本归属:10 引入,11 扩展01
var 由 JDK 10 的 JEP 286 引入。JDK 11 的 JEP 323 只把 var 的使用范围扩展到隐式类型的 lambda 参数,并允许在这些参数上写注解。把 var 说成「JDK 11 新特性」是常见的版本错误。
| 能力 | 最早版本 | JDK 11 状态 | 说明 |
|---|---|---|---|
| 局部变量、for、try-with-resources 中的 var | 10 | 可用 | JEP 286 |
| lambda 参数上的 var 与注解 | 11 | 可用 | JEP 323;所有参数必须同时写 var |
| catch 参数上的 var | 不支持 | 编译错误 | catch (var e) 在 JDK 11 报 'var' is not allowed here |
| 字段、方法参数、返回值上的 var | 不支持 | 编译错误 | var 只用于局部变量 |
| record / sealed / 文本块 | 不在 11 | 不存在 | 16 / 17 / 15 才正式 |
var 不是关键字,而是保留类型名(reserved type name):不能用来声明类、接口或枚举,但仍可以作为变量名或方法名。var var = 7; 在 JDK 11 可以编译,只是不该这么写。
var name = "Java"; // String
var count = 3; // int
var list = new ArrayList<String>(); // ArrayList<String>
var var = 7; // 合法,但不建议
推断发生在编译期,不是运行时02
var 的类型由编译器根据初始化表达式推断,并写入字节码的局部变量表。运行时没有类型检查,也没有反射之外的「动态」行为。它减少的是类型噪音,不是类型安全。
import java.util.ArrayList;
public class VarInference {
public static void main(String[] args) {
var list = new ArrayList<String>();
list.add("Java");
System.out.println(list);
var anon = new Object() {
String greeting() { return "hi"; }
};
System.out.println(anon.greeting());
var n = 3;
System.out.println(n + 2);
}
}
[Java] hi 5
匿名类是一个例外:var anon = new Object() { ... } 会保留匿名类的具体类型,因此可以调用 greeting()。如果写成 Object anon = ...,就只能看到 Object 的成员。
var 只是少写了一次类型名。不能用的位置:没有初始值就没有类型03
var 的推断依赖初始化表达式。没有初始化、初始化值为 null、或使用数组初始化简写时,编译器拿不到足够信息,只能报错。
public class BadVar {
public static void main(String[] args) {
var x = null;
var a = {1, 2};
}
}
BadVar.java:3: error: cannot infer type for local variable x
var x = null;
^
(variable initializer is 'null')
BadVar.java:4: error: cannot infer type for local variable a
var a = {1, 2};
^
(array initializer needs an explicit target-type)
2 errors
- 字段、方法参数、构造器参数、返回值都不能用 var;这些位置没有「局部变量初始化」的上下文。
- catch 参数在 JDK 11 也不支持 var,会报
'var' is not allowed here。 - 不能一条语句声明多个 var:
var a = 1, b = 2;不合法。 var不能作为类名;它是保留类型名,不是普通标识符。
import java.io.ByteArrayInputStream;
public class VarTry {
public static void main(String[] args) throws Exception {
try (var in = new ByteArrayInputStream("ab".getBytes())) {
System.out.println(in.read());
}
}
}
97
lambda 参数:11 的唯一扩展04
JDK 11 允许在隐式类型的 lambda 参数上写 var,主要目的不是省略类型,而是给参数加注解。一旦使用 var,所有参数都必须写 var,不能混用。
import java.util.function.BiFunction;
import java.util.function.Function;
public class VarLambda {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add = (var x, var y) -> x + y;
System.out.println(add.apply(2, 3));
Function<String, String> upper = (@Deprecated var s) -> s.toUpperCase();
System.out.println(upper.apply("ok"));
}
}
5 OK
@Deprecated 在这里只是演示注解位置,实际项目应换成参数校验或空值分析注解。没有 var 时,隐式类型参数不能直接写注解;这是 JEP 323 的实际价值。
BiFunction<Integer, Integer, Integer> f = (var x, y) -> x + y;
error: invalid lambda parameter declaration (cannot mix 'var' and implicitly-typed parameters)
(Integer x, Integer y),再加 var 没有意义;var 只用于隐式类型参数。什么时候该用:可读性优先05
var 的取舍标准只有一条:右侧是否让类型一目了然。如果读者需要跳到方法签名才能知道类型,写显式类型更好。
| 写法 | 建议 | 原因 |
|---|---|---|
var list = new ArrayList<String>() | 推荐 | 右侧已经写明泛型 |
var user = repository.findById(id) | 谨慎 | 类型可能很重要,显式写出更清楚 |
var n = 0 | 不推荐 | 没有减少多少噪音,反而丢了类型信息 |
var x = flag ? 1 : "a" | 避免 | 推断出的是交集类型,可读性差 |
var a = new int[]{1, 2} | 可以 | 数组类型明确;不能写 var a = {1, 2} |
var list = new ArrayList<>() | 注意 | 推断为 ArrayList<Object>,取出的元素是 Object |
import java.util.ArrayList;
public class VarDiamond {
public static void main(String[] args) {
var list = new ArrayList<>();
list.add("a");
String s = list.get(0);
System.out.println(s);
}
}
error: incompatible types: Object cannot be converted to String
new ArrayList<>() 没有目标类型可供推断,var 只能得到 ArrayList<Object>。需要泛型时,把类型写在右侧:var list = new ArrayList<String>();。
版本边界BOUNDARY
这张表比 API 本身更值得记。写代码时先确认你的运行时版本。
| 特性 / API | 状态 | 说明 |
|---|---|---|
| var 局部变量 | 10 正式 | JEP 286;JDK 11 可用,但不是 11 引入。 |
| var lambda 参数 | 11 正式 | JEP 323;所有参数必须同时写 var,可加注解。 |
| var catch 参数 | 不支持 | JDK 11 编译报 'var' is not allowed here。 |
| var 字段 / 参数 / 返回值 | 不支持 | 只能用于局部变量、for、try-with-resources。 |
| var 作为类名 | 不允许 | var 是保留类型名;作为变量名仍合法。 |
| record / sealed | 不在 11 | record 16 正式,sealed 17 正式。 |
费曼自检SELF CHECK
先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。
01. var 是 JDK 11 的新特性吗?
02. var 和动态类型有什么区别?
03. 为什么 var x = null 和 var a = {1, 2} 编译失败?
new int[]{1, 2}。