泛型擦除导致运行时classcastexception,因编译器隐式插入强制转换;list与list擦除后均为list,jvm无法区分,赋值不报错但取值调用时崩。

泛型擦除本身不抛异常,但它让编译器在你没写强制转换的地方,悄悄插进一句 (String)、(Number) 这样的转换——等真正执行到那行时,对象类型对不上,ClassCastException 就突然爆发。这种错误不发生在赋值时,而发生在取值或调用方法的那一刻,非常隐蔽。
擦除后“类型一致”的假象
运行时 List<string></string> 和 List<integer></integer> 都是 List,JVM 看不出区别。所以你可以把一个装了 Integer 的原始 ArrayList 直接赋给 List<string></string> 变量,编译器只给警告,不报错。但只要你调用 strList.get(0).length(),字节码里早已埋好的 (String) 转换就会尝试把 Integer 强转成 String,立刻崩。
隐式转换藏在哪儿?
这些 cast 不是你写的,是编译器自动加的,常见位置包括:
- 从泛型集合取元素:如
list.get(i)→ 编译后变成(T) list.get(i) - 泛型方法返回值:如
<t> T getValue()</t>调用后被转为具体类型 - 泛型字段读取:如
box.get()返回前会插入对应类型的 cast - 反射调用后强转:比如
(List<user>) method.invoke(obj)</user>,实际返回可能是ArrayList<linkedhashmap></linkedhashmap>
典型失配源头
以下操作看似无害,实则为运行时异常埋雷:
- 用原始类型(
List raw = new ArrayList())添加异构对象,再转成参数化类型 - JSON 反序列化未传
TypeReference<list>></list>,默认得到List<object></object>或List<linkedhashmap></linkedhashmap> - MyBatis 用
Map<string string></string>接收数据库BIGINT字段,取值时转String不报错,但调用get("id").length()会因实际是BigInteger而失败 - 泛型工厂方法写成
return (List<t>) new ArrayList()</t>,T 在运行时是Object,后续任何强转都不可靠
怎么提前发现?
别等崩溃才查。重点关注三点:
- 异常消息里 “cannot be cast to” 前后的类型,就是擦除后暴露的真实失配点
- 堆栈最上层那行“安全”代码(比如
list.get(0).toString()),它背后藏着编译器插入的 cast - 顺藤摸瓜检查该变量的初始化路径:是否经过原始类型、反射、序列化、或跨模块传参?这些环节最容易丢失泛型信息










