Skip to content

局部变量类型推断

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;当类型需要猜或翻文档时,显式写出类型。

基于 VitePress 构建