泛型擦除后ArrayList<String>和ArrayList<Integer>运行时均为ArrayList,JVM无法识别泛型信息;不能用instanceof或getClass判断具体泛型类型,反射需通过ParameterizedType获取;数组不能直接泛型化;通配符用于编译期类型安全检查,不可用于实例化。

泛型擦除后,ArrayList<string></string> 和 ArrayList<integer></integer> 运行时确实是同一个类
Java 泛型在编译期就被擦除了,所有类型参数都会被替换成其上界(通常是 Object),所以 ArrayList<string></string> 和 ArrayList<integer></integer> 编译后都是 ArrayList,JVM 看不到泛型信息。
这意味着你不能在运行时做这样的判断:list.getClass() == ArrayList<string>.class</string> —— 后者语法都不合法,ArrayList<string>.class</string> 根本不存在。
- 反射获取泛型实际类型只能靠字段/方法签名里的
ParameterizedType,比如从Field.getGenericType()拿,不是从实例对象上直接问 - 数组不能是泛型的:写
new ArrayList<string>[10]</string>会报错,因为擦除后 JVM 不知道该分配什么类型数组;得用new ArrayList[10],但失去类型安全 -
instanceof不能带泛型:if (obj instanceof ArrayList<string>)</string>是语法错误,只能写if (obj instanceof ArrayList)
通配符 ?、? extends T、? super T 不是“更松的泛型”,而是独立的类型系统规则
通配符不是为了绕过擦除,而是编译器用来做**类型安全协变/逆变检查**的机制。它和 T 的行为完全不同:通配符不能出现在类定义的类型参数位置,也不能用于创建实例或调用泛型方法。
-
List>表示“某个未知类型的 List”,你能读(返回Object),但不能写(除了null) -
List extends Number>只能读,且读出来是Number或其子类(编译器保证),但不能 add 任何具体类型——连add(new Integer(1))都不被允许,因为可能是List<double></double> -
List super Integer>可以写Integer及其子类,但读出来只能当Object;它适合做“消费者”场景,比如Collection.copy()的目标集合
T 在方法签名里能推导,在构造器/静态上下文中会失效
T 是类型变量,只在声明它的作用域内有效。最常踩的坑是:以为写了 public class Box<t> { public T getValue() { ... } }</t> 就能在任意地方拿到 T 的真实类对象——其实不行,擦除后 getValue() 返回的就是 Object。
立即学习“Java免费学习笔记(深入)”;
- 泛型方法可推导:
<t> T pick(T a, T b)</t>调用时pick("a", "b")推出T = String,但这个推导只发生在编译期,方法体内仍无Class<t></t> - 静态方法/字段不能引用所在类的
T:因为静态属于类,而泛型类的每个T实例化都对应不同运行时类,静态成员必须共享,所以static T DEFAULT是非法的 - 想保留类型信息?只能显式传入
Class<t></t>参数,比如new Gson().fromJson(json, typeToken)里的TypeToken<list>>()</list>就是靠匿名子类绕过擦除
泛型数组、强制转型、原始类型混用是 ClassCastException 高发区
擦除让编译器无法在运行时校验泛型一致性,很多错误只在取值时爆发,而且堆栈不指向问题源头。
- 用原始类型(raw type)接收泛型对象:
ArrayList raw = new ArrayList<string>(); raw.add(new Date());</string>编译通过,但后续String s = list.get(0);才抛ClassCastException - 泛型数组创建常见写法
(List<string>[]) new ArrayList[10]</string>是 unchecked cast,警告不是摆设——一旦往里存了非ArrayList<string></string>实例,取出来转型就崩 - 多层嵌套泛型容易误判类型关系:比如
Map<string list>></string>传给期望Map<string list extends number>></string>的方法,看似合理,但实际不兼容,因为泛型是不可变的(invariant)
真正麻烦的不是记不住规则,而是错误总在数据流动几层之后才暴露,而且没有明确指向声明点。调试时得倒着查谁往集合里塞了不该塞的东西,而不是盯着报错那一行。









