一、类型擦除:泛型的「皇帝新衣」
比喻:Java的泛型在编译后就像被脱掉衣服的皇帝——代码里写的是List<String>,但运行时实际是List<Object>,类型信息被擦除了。
1. 类型擦除的本质
-
编译时严格检查:写代码时编译器会检查类型是否匹配。
List<String> list = new ArrayList<>(); list.add("Hello"); // ✅ 合法 list.add(123); // ❌ 编译报错 -
运行时裸奔:编译后的字节码中泛型类型被擦除,替换为原始类型(如
Object或指定边界类型)。// 编译前 List<String> list = new ArrayList<>(); // 编译后(等效代码) List list = new ArrayList(); // 类型变Object
2. 类型擦除的限制
-
不能创建泛型数组:
// 编译报错:不可创建泛型数组 List<String>[] array = new ArrayList<String>[10]; -
无法用
instanceof检查类型:if (list instanceof List<String>) {} // ❌ 编译报错 -
无法直接实例化泛型对象:
T obj = new T(); // ❌ 编译报错(不知道T的具体类型)
二、通配符:灵活的类型边界
比喻:通配符就像一张通行证,规定谁能进(extends)或谁可以接收(super)。
1. 三种通配符
| 通配符 | 含义 | 使用场景 |
|---|---|---|
<?> | 任意类型(未知类型) | 只读操作(如遍历) |
<? extends T> | T或T的子类(上界通配符) | 生产者(提供数据) |
<? super T> | T或T的父类(下界通配符) | 消费者(接收数据) |
2. PECS原则(Producer-Extends, Consumer-Super)
-
生产者(Producer)用
extends:// 从集合读取数据(生产数据) void printAll(List<? extends Number> list) { for (Number num : list) { System.out.println(num); } }- 可传入
List<Integer>、List<Double>等。
- 可传入
-
消费者(Consumer)用
super:// 向集合写入数据(消费数据) void addNumbers(List<? super Integer> list) { list.add(1); list.add(2); }- 可传入
List<Number>、List<Object>等。
- 可传入
3. 通配符实战对比
| 场景 | 错误写法 | 正确写法 |
|---|---|---|
| 读取数据 | List<Object> 接收List<String> | 用List<?>或List<? extends Object> |
| 写入数据 | List<String> 添加Integer | 用List<? super String> |
三、类型擦除的底层真相
1. 编译后代码对比
// 源码(泛型)
public class Box<T> {
private T value;
public void set(T value) { this.value = value; }
public T get() { return value; }
}
// 编译后(类型擦除)
public class Box {
private Object value; // T被擦除为Object
public void set(Object value) { ... }
public Object get() { return value; }
}
2. 边界处理(有界泛型)
// 源码(有界泛型)
public class NumberBox<T extends Number> {
private T value;
}
// 编译后
public class NumberBox {
private Number value; // T被擦除为Number
}
四、总结口诀
「泛型擦除是伪装,编译检查运行时忘
通配符分上下界,生产消费要分详
PECS原则记心间,灵活安全两不误!」
附:高频面试题
-
为什么Java要用类型擦除实现泛型?
- 兼容性:为了兼容JDK5之前的旧代码。
-
List<?>和List<Object>有什么区别?`List<?>是未知类型(只读),List<Object>是明确类型(可写任意对象)。
-
如何绕过类型擦除的限制?
- 通过反射或传递
Class<T>参数(如newInstance())。
- 通过反射或传递