局部变量类型推断
JDK 10 引入了一个看似简单但影响深远的功能——var 关键字。
java
// 以前:类型写在两边
List<String> list = new ArrayList<String>();
Map<String, List<Integer>> map = new HashMap<String, List<Integer>>();
// 现在:让编译器推断类型
var list = new ArrayList<String>();
var map = new HashMap<String, List<Integer>>();这不是动态类型。编译后,变量类型是确定的,var 只是省掉重复写类型名的繁琐。
基本用法
从变量初始化推断
java
public class VarBasicDemo {
public static void main(String[] args) {
// 编译器会推断出 String
var message = "Hello, World";
// 推断出 int
var count = 42;
// 推断出 ArrayList<String>
var names = new ArrayList<String>();
names.add("Alice");
names.add("Bob");
// 推断出 String[]
var array = new String[]{"a", "b", "c"};
// 推断出 HashMap<String, Integer>
var scores = new HashMap<String, Integer>();
// 在 for 循环中
for (var i = 0; i < 10; i++) {
System.out.println(i);
}
// 在增强 for 循环中
for (var name : names) {
System.out.println(name);
}
}
}和 Lambda 参数(JDK 11+)
java
import java.util.function.*;
public class VarLambdaDemo {
public static void main(String[] args) {
// ❌ 以前:不能给 Lambda 参数加类型(除非全部加)
// BiFunction<Integer, Integer, Integer> add = (var a, var b) -> a + b;
// ✅ JDK 11+:可以用 var
BiFunction<Integer, Integer, Integer> add = (var a, var b) -> a + b;
System.out.println(add.apply(3, 5)); // 8
// 也可以全部写类型
BiFunction<Integer, Integer, Integer> sub = (Integer a, Integer b) -> a - b;
// 混用不行
// (var a, Integer b) -> a + b // ❌ 编译错误
}
}使用场景
场景一:泛型链式调用
java
var result = list.stream()
.filter(s -> s.length() > 3)
.map(String::toUpperCase)
.collect(Collectors.toList());
// 不用写 List<String> result = ...
var map = people.stream()
.collect(Collectors.groupingBy(
Person::getDepartment,
Collectors.counting()
));
// 不用写 Map<String, Long> map = ...场景二:复杂泛型嵌套
java
// 以前
Map<String, List<Map<String, Object>>> complex =
new HashMap<String, List<Map<String, Object>>>();
// 现在
var complex = new HashMap<String, List<Map<String, Object>>>();场景三:方法引用链
java
// var 让代码更易读
var processor = new SomeClass();
var result = processor.getData()
.stream()
.filter(Objects::nonNull)
.map(this::transform)
.collect(Collectors.toList());限制与规则
必须立即初始化
java
// ❌ 编译错误:没有初始化
var message;
// ❌ 编译错误:初始化为 null
var message = null;
// ✅ 正确
var message = "Hello";
var message = getDefaultMessage();不能用于方法参数
java
// ❌ 编译错误
public void process(var input) { }
// ✅ 正确
public void process(String input) { }不能用于类字段
java
public class Example {
// ❌ 编译错误
private var field = "value";
// ✅ 正确
private String field = "value";
}不能用于数组初始化
java
// ❌ 编译错误:数组初始化上下文不支持 var
var array = {1, 2, 3}; // ❌ 不行
// ✅ 正确
var array = new int[]{1, 2, 3};
var array = new String[]{"a", "b", "c"};不能连续声明多个变量
java
// ❌ 编译错误
var a = 1, b = 2, c = 3;
// ✅ 正确
var a = 1;
var b = 2;
var c = 3;什么时候用 var
推荐用 var
java
// 1. 类型名很长或很复杂
var result = list.stream().collect(Collectors.toMap(
Person::getId,
Function.identity()
));
// 2. 右边已经明确表明类型
var names = new ArrayList<String>();
// 3. 链式调用返回复杂泛型
var result = createProcessor().process().getResult();
// 4. 临时变量,用完就丢
var temp = calculate();
process(temp);不推荐用 var
java
// 1. 基本类型,简单明了
int count = 0; // ✅ 比 var count = 0; 好
String name = "Tom"; // ✅
// 2. 返回值,需要文档化
public User findById(Long id); // ✅ 明确知道返回 User
var user = findById(id); // ❌ 不知道返回什么类型
// 3. 变量在长代码中被多次使用
var result = calculate(); // 如果代码很长,下面的读者不知道 result 是什么
for (int i = 0; i < 10; i++) {
process(result);
}
// 4. 需要在注释中说明类型
var user = findById(id); // var user = findById(id); ✅ 直接看代码
var user = findById(id); // User user = findById(id); ❌ 加注释说明类型最佳实践
java
public class VarBestPractices {
public static void main(String[] args) {
// ✅ 好的用法
var result = computeExpensiveResult(); // 方法名已经表明类型
// ✅ 类型从右边明显可见
var list = new ArrayList<String>();
var map = new HashMap<String, Object>();
// ❌ 不好的用法
var x = getValue(); // 不知道 x 是什么
// ✅ 好的用法
var user = new User(); // 构造函数名就是类型
// ❌ 不好的用法
var temp = calculateSomething(); // 命名本身不清楚
// 改成
var sum = calculateSomething(); // 好多了
}
}小结
var 是 JDK 10 引入的一个小功能,但用好了能让代码更简洁:
| 适合用 var | 不适合用 var |
|---|---|
| 复杂泛型 | 基本类型 |
| 右边明确表明类型 | 返回值需要文档化 |
| 链式调用 | 长代码中的中间变量 |
| 临时变量 | 需要在注释中说明类型 |
记住:代码的可读性比少打字更重要。当类型从变量名或赋值右边能直接看出来时,用 var;当类型需要猜或翻文档时,显式写出类型。
