📌 JDK 版本要求:本文基于 JDK 10+ 编写并验证。
- var 作为局部变量类型推断关键字,于 JDK 10 正式发布;
- JDK 11/17/21 完全兼容;
- 仅适用于局部变量,不适用于字段、参数或返回值。 建议使用 JDK 17 或更高 LTS 版本。
“以前写一行 Map,要写三行长泛型;现在一个 var,清爽如初恋。” —— 后端老哥的自我修养
今天这篇文章就来瞅瞅用 var 如何写出更少噪音、更多信号的 Java 代码!

一、祖传泛型有多啰嗦?
在 JDK 10 之前,创建一个嵌套泛型集合是这样的:
Map<String, List<Map<String, Object>>> config =
new HashMap<String, List<Map<String, Object>>>();
或者用 钻石操作符 (JDK 7+)稍作缓解:
Map<String, List<Map<String, Object>>> config = new HashMap<>();
问题依然存在:
- 左边重复右边:类型信息写了两遍;
- 视觉噪音大:真正重要的逻辑被泛型符号淹没;
- 重构成本高:改一次类型,左右都要动。
这不是代码,这是类型仪式——Java 被诟病“啰嗦”的根源之一。

二、var 来了:让编译器替你干活
从 JDK 10 起,你可以这样写:
var config = new HashMap<String, List<Map<String, Object>>>();
编译器自动推断 config 的类型为 Map<String, List<Map<String, Object>>>。
关键特性:
- 仅限局部变量:不能用于字段、方法参数、返回值;
- 必须初始化:var x; 非法,必须 var x = …;;
- 类型在编译期确定:运行时无任何差异,零性能开销;
- 保留强类型语义:config.put(123, …) 仍会编译报错。
var 不是动态类型!它只是编译期的语法糖,Java 依然是强静态类型语言。

三、实战演示:var 让代码更聚焦意图
示例 1:Stream 链式调用
// 传统写法
Stream<String> stream = list.stream()
.filter(s -> !s.isEmpty())
.map(String::trim)
.sorted();
// 使用 var
var stream = list.stream()
.filter(s -> !s.isEmpty())
.map(String::trim)
.sorted();
效果:去掉冗余类型声明,突出数据流逻辑本身。
示例 2:Builder 模式
// 传统
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com"))
.timeout(Duration.ofSeconds(10))
.GET()
.build();
// 使用 var
var request = HttpRequest.newBuilder()
.uri(URI.create("https://api.example.com"))
.timeout(Duration.ofSeconds(10))
.GET()
.build();
效果:避免重复 HttpRequest,让配置过程成为焦点。
示例 3:复杂泛型 DTO
// 传统
List<CompletableFuture<Map<String, User>>> futures = fetchUserBatches();
// 使用 var
var futures = fetchUserBatches();
效果:当方法名已足够表达语义时,类型声明反而是干扰。
四、var 的边界:什么情况下不该用?
var 不是万能钥匙。以下场景应避免使用:
1. 初始化表达式无法清晰表达类型
var data = get(); // get() 返回 Object?List?Map?
// → 类型模糊,需跳转查看 get() 定义
改为显式声明:
Map<String, Integer> data = get(); // 一目了然
2. 使用菱形操作符但无上下文
var list = new ArrayList<>(); // 推断为 ArrayList<Object>!
list.add("hello");
list.add(42); // 编译通过!但可能非预期
明确泛型:
var list = new ArrayList<String>(); // 推断为 ArrayList<String>
3. 在 for-each 循环中牺牲可读性
for (var item : items) { ... } // 如果 items 是 List<?>,item 类型不明
若集合类型不明显,保留显式:
for (String item : items) { ... }
五、看看编译后的 var 字节码
字节码对比:
// var 写法
var name = "苏渡苇";
// 显式写法
String name = "苏渡苇";
两者生成完全相同的字节码:
ldc "苏渡苇"
astore_1
🔍 var 在编译后会被替换为精确的静态类型,JVM 无感知。
JIT 行为:
- 无额外检查;
- 无内存分配差异;
- 逃逸分析、内联优化等完全一致。
💡 结论:var 是纯编译期特性,安全、高效、零成本。
六、最佳实践:5 条黄金法则
用在类型冗长但初始化清晰的场景
如泛型、Builder、工厂方法返回值。
确保初始化表达式能“自解释”类型
方法名、构造器、字面量应足够暗示类型。
不要为了少打字而用 var
可读性 > 键盘省力。如果显式类型更清晰,就写出来。
配合现代 API 使用效果更佳
如 List.of(), Map.of(), HttpClient, Optional 等。
团队统一规范
避免有人全用 var,有人全不用,造成风格割裂。
七、常见误区澄清
| “var 是弱类型?” | ❌ 它是强类型,只是由编译器推断 |
| “会影响调试?” | ❌ IDE 和调试器都能正确显示推断类型 |
| “和 JavaScript 的 var 一样?” | ❌ JS 的 var 是动态作用域,Java 的 var 是静态类型、块级作用域 |
| “能用于 lambda 参数?” | ❌ 不能。但 JDK 11 起支持 (var x) -> …,用于显式标注 lambda 类型 |
八、代码在哪?
本篇涉及到的代码已上传至 GitHub:
https://github.com/iweidujiang/java-tricks-lab/tree/main/07-var-type-inference
欢迎 star & fork,下期继续!



