泛型擦除使List在运行时变为原始List,与List等无区别;JVM仅处理Object类型,编译器自动插入强制转换,类型安全仅限编译期,反射无法区分具体泛型参数。

泛型擦除让 List<string></string> 在运行时完全失去类型标识,它和 List<integer></integer>、List<object></object> 一样,最终都变成原始类型 List。JVM 看不到任何泛型信息,所有元素操作都基于 Object 进行。
运行时集合本质是原始 List
编译后,List<string></string> 的字节码中不再存在 String 标记。调用 add() 或 get() 时,底层实际处理的是 Object 类型:
-
list.add("hello")→ 实际执行list.add((Object)"hello") -
String s = list.get(0)→ 实际执行String s = (String) list.get(0),强制转换由编译器自动插入 - 若通过原始类型引用操作(如
List raw = stringList),可向其中加入任意对象(如raw.add(123)),但取值时会抛出ClassCastException
类型安全只在编译期保障
Java 不在运行时校验泛型一致性,所以:
- 反射调用
getClass()时,new ArrayList<string>().getClass() == new ArrayList<integer>().getClass()</integer></string>返回 true - 字段或方法签名中的泛型(如
private Map<string integer> cache;</string>)可通过反射获取,但集合实例本身不保留元素类型 - 无法创建泛型数组(
new T[10]编译失败),因为运行时不知道T是什么
为什么元素被当作 Object 处理
擦除规则决定:无边界类型参数(如 T)统一替换为 Object;有边界则替换为第一个上界(如 T extends Number → Number)。因此:
-
List<string></string>擦除后等效于List,内部存储结构仍是Object[] - 所有泛型集合的底层数组都是
Object[],没有String[]或Integer[]的专用实现 - 这种设计保证了与 JDK 5 之前代码的二进制兼容性,但牺牲了运行时类型精确性
不复杂但容易忽略










