1034 字
约 3 分钟
6
S04-J04 · 泛型:擦除、通配符与它的代价

适用读者:被 ClassCastException 或"泛型数组不能创建"卡住的人。
前置:S04-B06。
实测环境:OpenJDK 21.0.12.1(反射实测,2026-11-05)。
覆盖旧文:—(E 驱动;旧素材未覆盖泛型)
覆盖扩展:E-S04-015(类型擦除)、E-S04-016(PECS)。
上一篇:S04-J03 · 类文件结构与字节码
下一篇:S04-J05 · SPI 与运行时可扩展。


1. 一句话结论

Java 的泛型是"编译期检查 + 运行期擦除":编译器帮你检查类型,编译完泛型参数就被抹掉了。 所以运行时 List<String> 与 List<Integer> 是同一个类。

实测:

1. List<String> 与 List<Integer> 运行时类相同? true
2. 泛型参数(反射可见): [java.util.List<java.lang.String>]
3. 擦除后实际参数类型: [interface java.util.List]
4. 绕过泛型检查后 ls = [123](编译期警告,运行期不报)

2. 擦除的三个后果(E-S04-015)

后果 说明
运行时无类型信息 不能 instanceof List<String>、不能 new T[]
反射可绕过检查 实测第 4 条:塞进 Integer 也不报错
重载冲突 f(List<String>) 与 f(List<Integer>) 擦除后签名相同 → 编译错误

为什么这么设计:向后兼容——泛型是 Java 5 引入的,擦除让新代码能跑在旧 JVM 上,也让旧库无需重写。

语言 泛型实现
Java 擦除(运行期无信息)
C# 具化(运行期有信息,可 new T[])
Go 1.18+ 泛型(编译期实例化,待核细节)

3. 通配符与 PECS(E-S04-016)

List<? extends Number> src  // 只能读(生产者)
List<? super Integer>  dst  // 只能写(消费者)

PECS = Producer Extends, Consumer Super

场景 用什么 能做什么
只读(从集合取) ? extends T 读 ✅ 写 ❌
只写(往集合放) ? super T 写 ✅ 读只能当 Object
读写都要 具体类型 T 都行

经典应用:Collections.copy(List<? super T> dest, List<? extends T> src)。


4. 泛型与数组:为什么不兼容

T[] arr = new T[10];        // ❌ 编译错误(擦除后不知道 T 是什么)
List<T>[] lists = ...;      // ❌ 同理
替代方案 说明
List<T> 优先用集合代替数组
Array.newInstance(clazz, n) 反射创建(需传 Class)
@SuppressWarnings("unchecked") 强制转换(要注释说明为什么安全)

根因:数组是协变且保留运行时类型(String[] 是 Object[] 的子类型,且能检查 ArrayStoreException);泛型是不变且擦除。两者语义冲突,所以不能混。


5. 实践建议

建议 原因
不要用原始类型(List 而非 List<?>) 丢失全部编译期检查
优先 List<T> 而非 T[] 避开数组与泛型的冲突
通配符只在API 签名用 内部实现用具体类型更简单
强制转换处写注释 @SuppressWarnings 要有理由

6. 五个误区

误区 真相
"泛型在运行时生效" 编译期检查,运行期擦除
"instanceof List<String> 可以" 不可以(擦除)
"泛型能创建数组" 不能(用反射或集合)
"? extends 既能读又能写" 只能读
"原始类型只是警告" 会丢检查,可能在运行期炸

7. 自检

  1. 为什么 List<String> 与 List<Integer> 运行时是同一个类?
  2. 反射为什么能绕过泛型检查?
  3. PECS 分别对应什么场景?
  4. 为什么泛型不能创建数组?
  5. 原始类型(List)有什么风险?

8. 下一篇

S04-J05 · SPI 与运行时可扩展系统设计

泛型讲完,下一篇讲"框架怎么做到加个 jar 就生效"——ServiceLoader 与实测的两个实现被发现。


本篇属于系列《S04 · Java 语言核心》。扩展点 E-S04-015/016。四条反射实测为 OpenJDK 21。

S04-J04 · 泛型:擦除、通配符与它的代价
https://www.clxhxhhr.top/posts/5227/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。