类型擦除
Java 泛型通过类型擦除实现,泛型信息在编译时被擦除。
类型擦除原理
编译前
Java
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
Box<String> box = new Box<>();
box.set("Hello");
String s = box.get();
编译后(擦除)
Java
public class Box {
private Object value; // T 擦除为 Object
public void set(Object value) { this.value = value; }
public Object get() { return value; }
}
Box box = new Box();
box.set("Hello");
String s = (String) box.get(); // 编译器插入转型
类型擦除规则:无界类型参数
<T>擦除为 Object,有界<T extends Number>擦除为 Number。
擦除规则
| 泛型声明 | 擦除后类型 |
|---|---|
<T> | Object |
<T extends Number> | Number |
<T extends Comparable<T>> | Comparable |
多个边界 <T extends A & B> | 第一个边界 A |
类型擦除验证
Java
List<String> strings = new ArrayList<>();
List<Integer> integers = new ArrayList<>();
// 运行时两者类型相同
System.out.println(strings.getClass()); // ArrayList
System.out.println(integers.getClass()); // ArrayList
System.out.println(strings.getClass() == integers.getClass()); // true
运行时无泛型信息:泛型类型在运行时被擦除,ArrayList 和 ArrayList 运行时都是 ArrayList。
类型擦除的影响
不能创建泛型数组
Java
// 错误:不能创建泛型数组
List<String>[] array = new List<String>[10]; // 编译错误
// 原因:类型擦除后无法保证类型安全
// 擦除后 List<String>[] → List[],可放入 List<Integer>
不能创建泛型实例
Java
// 错误:不能 new T()
public class Box<T> {
public T create() {
return new T(); // 编译错误
}
}
// 解决:传入 Class 或 Supplier
public T create(Class<T> clazz) throws Exception {
return clazz.newInstance();
}
不能使用基本类型
Java
// 错误:泛型不支持基本类型
List<int> list = new ArrayList<int>(); // 编译错误
// 正确:使用包装类
List<Integer> list = new ArrayList<>();
静态成员不能使用类泛型参数
Java
public class Box<T> {
private static T value; // 错误:静态成员不能用 T
public static void method(T item) {} // 错误
// 正确:静态方法可定义自己的泛型参数
public static <E> void method(E item) {}
}
获取泛型信息
虽然运行时擦除,但可通过反射获取部分泛型信息:
Java
public class MyClass {
private List<String> field;
}
// 获取字段的泛型类型
Field field = MyClass.class.getDeclaredField("field");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
Type[] actualTypes = pt.getActualTypeArguments();
System.out.println(actualTypes[0]); // String.class
}
Signature 属性:编译器在字节码中保留泛型签名信息,反射可通过 Signature 获取。
桥方法
类型擦除后,编译器生成桥方法保证多态:
Java
public class StringBox extends Box<String> {
@Override
public void set(String value) { ... }
@Override
public String get() { ... }
}
// 编译器生成桥方法
public void set(Object value) { set((String) value); } // 桥方法
public Object get() { return get(); } // 桥方法
要点总结
- Java 泛型通过类型擦除实现
- 无界
<T>擦除为 Object,有界擦除为边界类型 - 运行时 ArrayList 和 ArrayList 类型相同
- 不能创建泛型数组、泛型实例
- 泛型不支持基本类型,需用包装类
- 静态成员不能使用类泛型参数
- 反射可通过 Signature 获取部分泛型信息
- 桥方法保证多态正确工作
📝 发现内容有误?点击此处直接编辑