bigywater.tech
Java · 276

var 的类型推断边界

var 省掉了类型声明,但推断出的类型有时出人意料:钻石、通配符、条件表达式、匿名类。这篇用 javac 报错信息反推每一个例子的推断类型,再验证 var 在哪些位置不能用、字节码里有没有 var。

ArrayList
var x = new ArrayList<>() 推断出的类型
611 字节
var 与显式类型写法编译出的 T.class 大小(完全相同)
9 种
本篇验证的 var 不能使用的写法数量

推断出的类型

实测 · 本机执行输出
  var x = 1;                                           → int
  var x = 1L;                                          → long
  var x = 1.5f;                                        → float
  var x = 'c';                                         → char
  var x = "s";                                         → String
  var x = new ArrayList<String>();                     → ArrayList<String>
  var x = new ArrayList<>();                           → ArrayList<Object>
  var x = List.of(1, 2, 3);                            → List<Integer>
  var x = List.of(1, "a");                             → List<INT#1> ;INT#1 extends Object,Serializable,Comparable<? extends INT#2>,Constable,ConstantDesc ;INT#2 extends Object,Serializable,Comparable<?>,Constable,ConstantDesc
  var x = Arrays.asList(1, 2.5);                       → List<INT#1> ;INT#1 extends Number,Comparable<? extends INT#2>,Constable,ConstantDesc ;INT#2 extends Number,Comparable<?>,Constable,ConstantDesc
  var x = wl.get(0);                                   → Number
  var x = wl;                                          → List<CAP#1> ;CAP#1 extends Number from capture of ? extends Number
  var x = m.get("k");                                  → List<Integer>
  var x = m.entrySet().iterator().next();              → Entry<String,List<Integer>>
  var x = true ? 1 : "s";                              → INT#1 ;INT#1 extends Object,Serializable,Comparable<? extends INT#2>,Constable,ConstantDesc ;INT#2 extends Object,Serializable,Comparable<?>,Constable,ConstantDesc
  var x = true ? new A() : new B();                    → INT#1 ;INT#1 extends Object,I,Serializable
  var x = new Object() { int n = 1; };                 → <anonymous Object>
  var x = (Runnable & Serializable) () -> {};          → INT#1 ;INT#1 extends Object,Runnable,Serializable
  var x = Optional.empty();                            → Optional<Object>
  var x = new int[]{1, 2};                             → int[]
  var x = (byte) 1;                                    → byte
  byte b0 = 1; var x = b0 + b0;                        → int
  var x = 1 / 2.0;                                     → double
  var x = Collections.emptyList();                     → List<Object>
  var x = (Comparable<String>) "s";                    → Comparable<String>

var 不能用的地方

实测 · 本机执行输出
  var x = null;                  (局部变量初始化为 null)→ cannot infer type for local variable x
  var x;                         (没有初始化器)→ cannot infer type for local variable x
  var x = {1, 2};                (数组初始化器)→ cannot infer type for local variable x
  var x = () -> {};              (lambda)→ cannot infer type for local variable x
  var x = String::length;        (方法引用)→ cannot infer type for local variable x
  var x = 1, y = 2;              (同一条语句声明多个)→ 'var' is not allowed in a compound declaration
  var x[] = new int[1];          (var 与数组括号)→ 'var' is not allowed as an element type of an array
  var x = x + 1;                 (自引用)→ cannot infer type for local variable x
  var[] x = new int[1];          (var[])→ 'var' is not allowed as an element type of an array
  class T { var f = 1; }                                                           (字段)→ 'var' is not allowed here
  class T { var m() { return 1; } }                                                (方法返回类型)→ 'var' is not allowed here
  class T { void m(var p) {} }                                                     (方法参数)→ 'var' is not allowed here
  class var {}                                                                     (类型名 var)→ 'var' not allowed here
  class T { void f() { int var = 1; var var = 2; } }                               (变量名叫 var)→ variable var is already defined in method f()
  class T { void f() { try (var r = new java.io.StringReader("x")) { } } }         (try-with-resources)→ 通过
  class T { void f() { for (var i = 0; i < 1; i++) {} for (var s : java.util.Lis   (for 循环)→ 通过
  class T { void f() { java.util.function.BiFunction<Integer,Integer,Integer> g    (lambda 参数用 var)→ 通过
  class T { void f() { java.util.function.BiFunction<Integer,Integer,Integer> g    (lambda 参数部分用 var)→ invalid lambda parameter declaration
  class T { void f() { Object o = 1; if (o instanceof var x) {} } }                (instanceof var)→ 'var' is not allowed here

var 与字节码

实测 · 本机执行输出
  使用 var 与显式类型的两份源码编译出的 T.class:字节数 611 / 611;完全相同 = True
  加 -g(保留局部变量表)后:相同 = True(var 的类型被写成它推断出的具体类型,局部变量表里没有 var 这个词)
  var 版本 -g 后的 LocalVariableTable: 0 5 0 this LT; | 43 7 3 v Ljava/lang/Integer; | 8 47 0 l Ljava/util/ArrayList; | 19 36 1 s I

var 的陷阱

实测 · 本机执行输出
  var l1 = new ArrayList<>() 的元素类型是 Object:可以同时加入 Integer 与 String → [1, s]
  对照 List<String> l2 = new ArrayList<>():元素类型 String
  who(cs)(声明类型 CharSequence)→ CharSequence;var cs2 = cs;who(cs2) → CharSequence(var 保留的是右边表达式的静态类型)
  var d = make()(返回 Animal)→ d 的静态类型是 Animal,不是 Dog:d.getClass() 是 Dog,但编译期只能调用 Animal 的方法
  var anon = new Object() { int hits = 3; … } 可以访问匿名类的成员:anon.hits = 4
  var lst = List.of(1, 2.5, 3L) 的元素静态类型是 Number & Comparable<…>;第一个元素的运行时类型 Integer
  var cond = true ? 1 : 2.0 的类型是 double(数值提升):值 = 1.0,类型 = Double
  var half = i / 4 → 2(int);var halfD = i / 4.0 → 2.5(double)
  var chars → char[];var bytes → byte[]
  var x = 100_000_000; var y = x * 100 → 1410065408(int 溢出,var 没有帮你换成 long)

分步演示

var 类型推断
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / OpenJDK(Temurin 17.0.20、Homebrew 21.0.12 与 27,每篇注明实际用的版本);用 javac 编译、java 直接运行,需要第三方库时用 Maven 解析依赖;计时用 System.nanoTime,数字每次运行会有小幅抖动。JDK 21.0.12 的 javac;推断类型通过「赋给 Thread.State 的编译错误」读出
    • 没有验证:IDE 里 var 的显示、var 在 lambda 参数里与注解的组合、var 对可读性的团队约定、JDK 10 之外版本里 var 的语法差异(JDK 11 引入 lambda 参数 var)、与 switch 模式匹配里类型推断的关系
    Java · var 的类型推断边界 · 实验记录 · 2026-10-03