Java 泛型擦除机制(Type Reasure)
· 阅读需 2 分钟
Java 编译器在编译时会移除所有的泛型类型信息。
case1: 编译时 vs 运行时
// 编译时(源代码)
List<String> stringList = new ArrayList<String>();
List<Integer> intList = new ArrayList<Integer>();
// 运行时(字节码)- 泛型信息被擦除
List stringList = new ArrayList(); // 变成 Object
List intList = new ArrayList(); // 变成 Object
// 验证:运行时类型相同
System.out.println(stringList.getClass() == intList.getClass()); // true
case2: 泛型类型被替换
// 源代码
public class Box<T> {
private T value;
public T getValue() {
return value;
}
}
// 编译后等价于
public class Box {
private Object value; // T 被擦除为 Object
public Object getValue() {
return value;
}
}
case3: 有上界的泛型
// 源代码
public class NumberBox<T extends Number> {
private T value;
public T getValue() {
return value;
}
}
// 编译后等价于
public class NumberBox {
private Number value; // T 被擦除为上界类型 Number
public Number getValue() {
return value;
}
}
带来的问题:
- 无法获取泛型的类型
public class Test {
public <T> void printType(List<T> list) {
// ❌ 编译错误:无法获取 T 的实际类型
// System.out.println(T.class);
// ❌ 运行时只能获取到 List,不知道 T 是什么
System.out.println(list.getClass()); // class java.util.ArrayList
}
}
- 无法重载
public class Example {
// ❌ 编译错误:方法签名冲突
public void method(List<String> list) { }
public void method(List<Integer> list) { } // 擦除后签名相同
}
解决方案: 使用 hutool 的 cn.hutool.core.util.TypeUtil#getTypeArgument(java.lang.reflect.Type, int) 方法可以获取到泛型类型。
需要注意的是:Lambda 会丢失泛型信息,具体匿名内部类和实现类(public xxx implement xxx)不会。