泛型擦除后ArrayList和ArrayList运行时完全相同,均为原始类型ArrayList,JVM无法区分其泛型参数,导致instanceof失效、无法new T()、getClass()返回相同Class对象,且反射无法获取实际类型参数。

泛型擦除后 ArrayList<string></string> 和 ArrayList<integer></integer> 运行时完全一样
Java 泛型在编译期就被擦除了,所有类型参数都被替换成上界(通常是 Object),所以 ArrayList<string></string> 和 ArrayList<integer></integer> 编译后都是裸的 ArrayList,JVM 看不到任何泛型信息。
这意味着你无法在运行时通过 instanceof 判断泛型类型,也不能用 new T() 创建泛型实例——因为 T 已经没了。
-
ArrayList<string>.class == ArrayList<integer>.class</integer></string>返回true -
list.getClass().getTypeParameters()拿不到实际类型参数,只返回声明时的形参名(如T) - 反射获取字段或方法的泛型签名要用
getGenericType(),而不是getType()
桥接方法是编译器为兼容多态自动补的“翻译层”
当泛型类继承/实现带泛型的方法(比如 List<e></e> 的 add(E)),而子类重写时用了具体类型(如 add(String)),JVM 会发现签名不匹配——因为擦除后父类方法是 add(Object),而子类写的是 add(String)。这时编译器悄悄生成一个桥接方法 add(Object),内部强制转型再调用你的 add(String)。
你可以用 javap -c 看到它,方法上有 ACC_BRIDGE ACC_SYNTHETIC 标志。
立即学习“Java免费学习笔记(深入)”;
- 桥接方法不可见于源码,但会影响反射:调用
Method.isBridge()能识别它 - Spring AOP 或某些代理框架如果没跳过桥接方法,可能重复拦截或出错
- 泛型接口默认方法不会触发桥接;只有继承链中存在类型擦除差异时才需要
想绕过擦除?只能靠运行时能拿到的“残留信息”
真正能用的只有那些没被擦掉的泛型结构:字段、方法参数、返回值、父类/接口的泛型实参。比如 Map<string list>> field</string> 中的完整嵌套类型,在字段上还能通过 Field.getGenericType() 取到 ParameterizedType。
- 用
new ArrayList<string>() {{ add("x"); }}</string>这种匿名子类写法,能让 JVM 记住父类实参(因为getClass().getGenericSuperclass()可读) - 函数式接口(如
Function<string integer></string>)作为参数传入时,lambda 本身不保留类型,但方法引用(String::length)在特定上下文中可能推导出 - Gson / Jackson 解析 JSON 时依赖字段上的泛型信息,不是靠对象实例的运行时类型
泛型数组创建失败的根本原因:擦除 + 类型检查时机错位
new ArrayList<string>[10]</string> 编译报错,不是语法问题,而是因为数组在运行时要记录组件类型(ArrayList.class),而泛型信息已擦除,JVM 无法确认这个数组该用什么类型做运行时检查。
更直白地说:数组是协变的(String[] 是 Object[] 子类型),但泛型是不变的(ArrayList<string></string> 不是 ArrayList<object></object> 子类型),两者逻辑冲突。
- 正确做法是用
new ArrayList[10](原始类型数组),或改用List<arraylist>></arraylist> - 用
@SuppressWarnings("unchecked") ArrayList<string>[] arr = (ArrayList<string>[]) new ArrayList[10];</string></string>能过编译,但有ClassCastException风险——尤其在存入不同泛型实例后取出来 - 泛型数组问题和泛型异常(
throws T)一样,都是擦除导致的语义断裂点,没有银弹
真正麻烦的从来不是“怎么写对”,而是“什么时候你以为它还存在,其实早就没了”。比如调试时看变量类型显示 ArrayList<string></string>,那只是 IDE 根据源码做的推测;一进字节码,就只剩 ArrayList。这种认知落差最容易在反射、序列化、动态代理场景里突然咬人一口。


















