泛型类型擦除是编译器将类型参数替换为上界(或Object)、插入强制转换、抹去类名中泛型信息的过程,旨在保障与Java 5前字节码的二进制兼容,而非简化代码。

泛型类型擦除不是“删代码”,而是编译器在保障类型安全的前提下,把泛型参数替换成真实可运行的类型,并抹去泛型标识的过程。它的核心不是为了简化,而是为了兼容——让带泛型的新代码能和 Java 5 之前没泛型的老字节码无缝共存。
类型擦除到底做了什么
编译器会做三件事:
- 把所有类、方法、字段里的类型参数(如 T、E)替换成它的上界;没有上界就换成 Object
- 在需要的地方自动插入强制类型转换,比如 list.get(0) 表面返回 String,实际字节码里是 (String) list.get(0)
- 擦除泛型类名中的类型信息,所以 ArrayList
和 ArrayList 编译后都是 ArrayList,getClass() 结果完全相同
为什么非得擦除不可
这不是设计偷懒,而是 JVM 层面的历史选择:
- Java 5 之前所有集合类(如 ArrayList、HashMap)都只操作 Object,字节码里根本没有泛型概念
- 如果泛型保留到运行时(像 C# 那样),List
和 List 就是两个不同类,老代码调用新泛型类会失败,破坏二进制兼容 - 擦除保证了:新写的 ArrayList
和老代码里的 ArrayList 是同一个类,能互相传参、继承、转型
擦除带来的限制,其实都是线索
这些“不能做”的事,恰恰印证了擦除的存在:
立即学习“Java免费学习笔记(深入)”;
- 不能 new T[] —— 因为运行时根本不知道 T 是什么类型,数组创建需要确切的组件类型
-
不能 if (obj instanceof List
) —— 运行时只有 List,没有 List这个类型 -
静态方法里不能用类声明的
—— 静态属于类,而泛型参数属于实例,擦除后类级别根本看不到 T
擦除不等于全丢,Signature 里还留着线索
虽然运行时无法通过 getClass() 拿到泛型,但编译器悄悄把泛型签名写进了字节码的 Signature 属性中。所以通过反射配合子类继承(比如 new TypeToken>() {}
),还是能还原部分泛型信息——这说明擦除是“逻辑擦除”,不是物理删除。


















