外观
一句话答案
Java 泛型在编译期检查类型安全,编译后擦除为 Object(或上界),运行时无法获取泛型实际类型。
核心要点
泛型的本质:编译期类型检查,运行期擦除
java
// 编译前(有泛型信息)
List<String> list = new ArrayList<>();
list.add("hello");
String s = list.get(0); // 编译器自动插入 (String) 强转
// 编译后(类型擦除,字节码中没有泛型信息)
List list = new ArrayList();
list.add("hello");
String s = (String) list.get(0); // 编译器插入的强制类型转换类型擦除规则:
① 无界泛型 <T> → 擦除为 Object
② 有界泛型 <T extends Number> → 擦除为 Number(上界)
③ 所有泛型类型参数在运行时消失,JVM 只看到原始类型类型擦除带来的限制:
java
// 1. 不能 new 泛型数组
T[] arr = new T[10]; // 编译错误
// 2. 不能 instanceof 泛型
if (obj instanceof List<String>) // 编译错误,运行时无法区分 List<String> 和 List<Integer>
// 3. 不能 new 泛型对象
T t = new T(); // 编译错误
// 4. 静态方法/字段不能使用类的泛型参数
class Foo<T> {
static T value; // 编译错误
}PECS 原则(Producer Extends, Consumer Super):
java
// Producer(读取数据)→ 用 extends(上界通配符)
// "生产"数据给我用,我只读不写
void printAll(List<? extends Number> list) {
for (Number n : list) { // 读取:安全,一定是 Number 或其子类
System.out.println(n);
}
// list.add(1); // 写入:编译错误!不知道具体是 List<Integer> 还是 List<Double>
}
// Consumer(写入数据)→ 用 super(下界通配符)
// "消费"我给的数据,我只写不读
void addNumbers(List<? super Integer> list) {
list.add(1); // 写入:安全,一定能接受 Integer
list.add(2);
// Integer n = list.get(0); // 读取:编译错误!只能读出 Object
}PECS 经典应用(Collections.copy 签名):
java
public static <T> void copy(List<? super T> dest, // 消费者:往里写
List<? extends T> src) { // 生产者:从中读
for (int i = 0; i < src.size(); i++)
dest.set(i, src.get(i));
}追问与易错
追问方向:
- 类型擦除后怎么保证类型安全?(编译期检查 + 强制类型转换由编译器插入)
- 运行时能获取泛型类型吗?(通过反射获取泛型父类/泛型字段的 ParameterizedType)
- 泛型数组为什么不能直接创建?(类型擦除后无法保证数组的运行时类型检查)
易错点:
- ❌ "Java 泛型和 C++ 模板一样"——Java 是擦除式泛型,运行时无类型信息
- ❌ 混淆
<? extends T>和<? super T>——PECS 原则:生产者 extends 消费者 super
💡 记忆锚点
泛型是编译期的安检门,过了门(编译后)类型标签就撕掉了(擦除为Object/上界),运行时JVM是"类型色盲"。读数据用extends(生产者),写数据用super(消费者),口诀PECS。