泛型擦除是Java为兼容旧代码的主动设计,编译期做类型检查,运行时仅保留原始类型;无界擦为Object,单上界擦为上界类型,多上界擦为第一个接口;运行时无法进行instanceof泛型判断、new T()、泛型数组创建及泛型方法重载。

泛型擦除不是 bug,而是 Java 为兼容老代码做的主动设计。它让编译期能做类型检查,运行时却只留下原始类型——比如 List 和 List 编译后都是 List,JVM 看不到泛型参数。
擦除是怎么发生的
编译器按规则把泛型参数“替换成具体类型”,同时插入强制转换来保安全:
-
无边界(如
)→ 擦成 Object :最常见,Box变成Box,字段和方法都用Object -
单上界(如
)→ 擦成上界类型 :value字段变成Number,方法返回值也是Number -
多上界(如
)→ 擦成第一个接口 :只保留Comparable,后面接口仅用于编译校验
哪些事运行时做不了
因为类型信息没了,这些操作会受限或报错:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
-
list instanceof List不合法,只能写list instanceof List -
new T()在泛型类或方法里直接写会编译失败,T 已经不存在 -
List编译不通过,得写成[] arr = new ArrayList [10] (List并接受警告[]) new ArrayList[10] - 不能靠泛型重载方法:两个方法
void f(List和) void f(List擦除后签名相同,编译报错)
怎么绕过擦除限制
真需要运行时知道类型,得靠“额外传参”或“利用继承结构”:
- 显式传入
Class:比如new Box,内部存起来用于反射或转型(String.class) - 用
TypeToken技巧:写new TypeToken,靠匿名子类的父类签名保留泛型信息,再通过反射提取- >() {}
- 从父类继承泛型声明:如果类是
class MyList extends ArrayList,那么getClass().getGenericSuperclass()能拿到ArrayList;但局部变量、方法返回值不行
实际编码要注意什么
别被编译期的“安全假象”骗了:
- 反射调用
add()可以往ArrayList里塞字符串,运行时不拦——擦除后就是ArrayList,add 接收Object - 强转泛型集合风险高:
List编译通过,但若实际是s = (List ) someList List,取元素时才抛ClassCastException - 静态方法/静态字段不能用类型参数:因为静态属于类,而擦除后所有泛型实例共享同一个原始类










