Java泛型擦除原理详解
泛型擦除发生在编译期,由javac完成而非JVM;无界类型参数擦除为Object,有界则取首个上界;擦除不可逆,导致instanceof、newT()等运行时操作失效,但部分元数据仍保留在class文件中供IDE和框架使用。
泛型擦除发生在编译期,由javac完成而非JVM;无界类型参数擦除为Object,有界则取首个上界;擦除不可逆,导致instanceof、new T()等运行时操作失效,但部分元数据仍保留在class文件中供IDE和框架使用。

泛型擦除发生在编译期,不是运行时
Java 泛型的“类型信息消失”不是 JVM 做的,而是 javac 编译器在生成字节码前就完成了擦除。你写的 List 和 List,在编译后都变成裸的 List 类型——它们的 getClass() 返回值完全相同,字节码里也找不到任何 String 或 Integer 的痕迹。
关键点在于:擦除是“不可逆”的预处理步骤,不是运行时丢弃。所以反射、instanceof、new T() 这些依赖运行时类型的操作,全部失效。
擦除规则很机械:看有没有上界,没上界就变 Object
编译器按固定规则替换类型参数,不推理、不猜测:
(无界)→ 擦除为Object→ 擦除为Number→ 擦除为Comparable(只取第一个接口)
例如这个类:
public class Box{ private T value; public void set(T value) { this.value = value; } public T get() { return value; } }
擦除后等效于:
public class Box {
private Runnable value;
public void set(Runnable value) { this.value = value; }
public Runnable get() { return value; }
}
为什么编译期还能报错?因为擦除前先检查
你以为 list.add(123) 报错是因为“运行时发现类型不对”,其实不是——它根本过不了编译。流程是:先做泛型语义检查 → 再擦除 → 最后生成字节码。
所以这些行为你得清楚:
List→ 编译失败,list = new ArrayList<>(); list.add(42); add方法签名被擦除为add(Object),但编译器在擦除前已判定42不符合String约束String s = list.get(0);→ 编译器自动补上(String)强转,字节码里能看到checkcast指令- 用反射调
add就能绕过检查,往List里塞Integer—— 因为反射跳过了编译期校验
擦除带来的硬限制,躲不开也改不了
这不是 bug,是设计契约。所有泛型相关限制都源于擦除不可逆:
- 不能写
if (list instanceof List→ 编译报错,语法不合法) - 不能声明
static中的T getValue() T→ 静态上下文看不到类型参数 - 不能
new T[]或new T()→ 运行时不知道T是什么类 - 泛型数组必须靠
Array.newInstance(clazz, size)+ 强制转型,且会带@SuppressWarnings("unchecked")
真正容易被忽略的是:泛型信息虽被擦除,但部分元数据(如 Signature、LocalVariableTypeTable)仍保留在 class 文件里——仅用于 IDE 提示、框架反射读取(如 Spring、Jackson),JVM 自身完全无视它们。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















