JDK JDK 学习手册8 → 27 · JDK 专用学习站
QUICKREF · LANGUAGE · JEP 286 (10) / JEP 323 (11)

var:局部变量类型推断

JDK 11 · 速查主题 01 / 06 · 预计 18 分钟
基础18 分钟JEP 286JEP 323
一句话var 是 JDK 10 的特性,不是 11。它把局部变量的类型交给编译器推断,类型在编译期就固定,字节码里没有「动态类型」。JDK 11 只做了一件事:允许 lambda 参数写 var,从而给参数加注解。

学完你要能自己回答

  1. var 是 JDK 11 的新特性吗?它推断出的类型在什么时候确定?
  2. 哪些位置不能用 var?var x = null 与 var a = {1, 2} 为什么编译失败?
  3. 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 中的 var10可用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 不会让 Java 变成动态语言。方法重载、泛型擦除、编译期类型检查都照常发生;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
try-with-resources 里的 var 很实用:资源类型通常从构造器一眼可见,写全名只会增加噪音。

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>();。

不要为了「看起来现代」而全局替换 var。团队约定通常比个人偏好重要;在公开 API、复杂泛型和领域类型上,显式类型更利于维护。

版本边界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不在 11record 16 正式,sealed 17 正式。

费曼自检SELF CHECK

先合上页面,用自己的话回答,再展开对照。能讲出因果和反例,才算真正掌握。

01. var 是 JDK 11 的新特性吗?
不是。var 局部变量推断来自 JDK 10 的 JEP 286;JDK 11 的 JEP 323 只允许 lambda 参数写 var 并加注解。
02. var 和动态类型有什么区别?
var 的类型在编译期由初始化表达式推断并写入字节码,之后不能再变;动态类型在运行时才确定,且可以重新赋值为其他类型。var 不改变 Java 的静态类型系统。
03. 为什么 var x = null 和 var a = {1, 2} 编译失败?
编译器需要初始化表达式提供类型。null 没有类型,数组初始化简写没有目标类型;两者都无法推断,必须写显式类型或 new int[]{1, 2}。
04. lambda 参数写 var 的实际价值是什么?
主要是允许在隐式类型参数上写注解(如空值分析、校验注解)。代价是所有参数都必须写 var,不能与省略类型的参数混用。
学习进度 记录保存在本机浏览器 · 不上传

JDK 学习手册 · JDK / Java 专用学习站 · 纯静态 · 双击 index.html 即可使用

本站专注 JDK 版本演进与 Java 教学,非 Oracle / OpenJDK 官方站点。版本数据来自 OpenJDK 官方 Features 表;课程示例已在对应本机 JDK 实测。构建日期 2026-11-12。