类型擦除的核心机制是Java编译期将泛型参数替换为原始类型(无界替为Object,有界替为上界),自动插入强制转换,并生成桥接方法以维持多态,最终字节码不含泛型信息,确保老JVM可直接运行。

类型擦除的核心机制,是Java在编译期主动剥离泛型类型参数,把所有泛型代码“降级”为JVM原生支持的非泛型字节码。它不靠运行时支持,也不改JVM,而是让新代码看起来和老代码一模一样——这才是兼容老版本字节码的根本逻辑。
编译期替换:统一转成原始类型
编译器遇到泛型,不做保留,只做替换:
- 无边界类型参数(如 T、E)一律替换成 Object
- 有上界类型参数(如 T extends Number)替换成上界类型(Number)
- 通配符(如 ? extends Runnable)擦除后也按上界处理,变成 Runnable
- 泛型集合如 ArrayList<String> 编译后只剩 ArrayList,内部数组仍是 Object[]
自动插入转换:补上擦掉的安全性
擦除后类型变宽了,但开发者写的代码仍要安全运行。编译器会在调用点悄悄加强制转换:
- list.get(0) 原本返回 String,擦除后返回 Object,编译器自动补上 (String) list.get(0)
- map.put("k", new Date()) 擦除后是 map.put("k", (Object) new Date()),类型检查在编译期完成
- 所有这类转换都由编译器生成,开发者看不到,但ClassCastException若发生,一定出现在运行时转换失败处
桥接方法:兜住多态断裂的风险
泛型类继承或实现接口时,擦除会导致方法签名不一致。桥接方法是编译器自动生成的“粘合层”:
立即学习“Java免费学习笔记(深入)”;
- 例如 class MyList<T> extends ArrayList<T>,父类擦除后是 ArrayList,其 add(Object) 和子类可能写的 add(T) 签名冲突
- 编译器会为子类生成一个桥接方法:public boolean add(Object x) { return add((T)x); }
- 这样通过父类引用调用时,多态依然成立,不会因擦除而断链
零字节码改动:老JVM完全无感
擦除后的字节码里没有泛型标记、没有类型参数表、没有Signature属性(除非显式保留)。JVM加载时看到的就是标准的类结构:
- List<String> 和 List<Integer> 编译后都是同一个 java.util.List 接口
- 老版本JVM(Java 1.4及之前)能直接加载、链接、执行这些字节码,无需任何升级或适配
- 所有兼容性代价由编译器承担,运行时零新增负担


















