类型擦除是编译期将泛型转换为原始类型并插入强制转换的过程,非运行时丢失;List擦除为List,T→Object或上界类,故无法instanceof判断、new T()、声明泛型数组或重载泛型方法。

Java 泛型中的类型擦除,不是“丢失”或“bug”,而是编译器主动执行的一套规则化处理过程——它让泛型在编译期发挥类型检查作用,又让运行时仍能和老 JVM、老代码完全兼容。
类型擦除的本质是编译期转换,不是运行时隐藏
你写的 List<string></string>,编译后字节码里根本不存在 <string></string> 这部分。JVM 看到的只是 List(原始类型),内部元素全按 Object 处理。但编译器没偷懒:它悄悄在 get() 返回处插入了 (String) 强制转换,保证你取值时不会写错类型。
- 擦除不是删光一切,而是有规则地替换:无界(
<t></t>)→Object;有上界(<t extends number></t>)→Number - 擦除发生在 .java → .class 的编译阶段,源码和字节码之间就完成了,和运行时无关
- 你用反射看到
list.getClass()是ArrayList而不是ArrayList<string></string>,这正是擦除完成后的正常表现
为什么不能用 instanceof 判断泛型类型
因为 instanceof 是运行时操作,而泛型类型信息在运行时已不存在。下面这行代码会直接编译失败:
if (obj instanceof List<string>) { ... }</string>
立即学习“Java免费学习笔记(深入)”;
编译器报错不是限制你,而是告诉你:这个判断在逻辑上无意义——JVM 根本没有 List<string></string> 这个类型概念。你可以写 obj instanceof List,因为 List 是擦除后的原始类型,真实存在。
常见误操作背后的真实原因
很多“奇怪限制”其实都源于擦除后类型不可知:
-
不能 new T():擦除后
T变成Object或某个上界类,但编译器无法确定具体是哪个子类,所以禁止实例化 -
不能声明泛型数组:如
new ArrayList<string>[10]</string>报错,因为数组在运行时需检查元素类型安全,而String信息已擦除 -
List
和 List :擦除后都是无法重载方法 List,方法签名重复,编译不通过
如何在需要时“找回”泛型类型
虽然运行时泛型被擦除,但部分结构(如继承自带泛型的父类)仍可通过反射获取原始泛型声明:
- 子类明确继承泛型父类时,比如
class MyList extends ArrayList<string> {}</string>,可用MyList.class.getGenericSuperclass()拿到ArrayList<string></string>的 Type - 字段或方法参数上的泛型注解(如
@JsonDeserialize(contentAs = String.class))是常用绕过手段 - 第三方库(如 Gson、Jackson)通过传入
TypeToken<list>>() {}</list>这种匿名子类方式,利用类加载时保留的泛型签名来恢复类型


















