泛型擦除与可具体化边界
泛型类型擦除如何工作:运行时可见什么、为何不能 new T[],以及桥接方法直觉。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
泛型擦除与可具体化边界
这个概念为什么出现
学了泛型仍会对 List<String> 运行时类型、异常捕获、数组创建感到困惑。根因是 Java 泛型主要是编译期机制,采用类型擦除兼容历史虚拟机。
[!abstract] 一句话理解 类型参数在编译后大多被擦除为上界(常为 Object),运行时集合主类型相同;因此不能依赖
T做可具体化操作,数组与异常等有专门限制。
最小可运行示例
场景:运行时看两个参数化列表的类
import java.util.*;
public class ErasurePeek {
public static void main(String[] args) {
List<String> a = new ArrayList<>();
List<Integer> b = new ArrayList<>();
System.out.println(a.getClass() == b.getClass()); // true
// if (a instanceof List<String>) {} // 编译不合法/无意义模式
System.out.println(a instanceof List);
}
}
javac ErasurePeek.java && java ErasurePeek
期望:
true
true
结合场景再看三个关注点
- 运行时类相同不代表编译期类型相同。
- 强制转换由编译器插入,不安全用法会在运行期炸。
- 需要保留类型时用超类型令牌/显式 Class 令牌等模式。
核心概念与准确模型
1. 擦除规则直觉
- 无界 → Object
T extends Number→ Number- 桥接方法保持多态
2. 不可做的事
new T()/new T[]- catch 可参数化异常类型
- 运行时
instanceof List<String>
3. reification
Java 数组可具体化;泛型参数默认不可。
边界情况与反直觉行为
- 反射
getGeneric*可看到签名信息(类文件属性),不等于完整运行时 reify。 - 原始类型触发未检查警告。
- 变参与泛型产生 heap pollution。
常见误区
[!warning] 常见误区:认为擦除=泛型没用 编译期安全与 API 表达仍然极有价值。
工程实践
- 避免 raw type。
- 需要运行时类型传
Class<T>/TypeToken。 - 抑制警告仅限边界。
- 读 javap 看桥接方法建立直觉。
本节总结
- 擦除解释运行时限制
- 编译期仍强约束
- 与数组 reify 对比记忆
自测题
- 为什么不能创建泛型数组
new T[n]? - 桥接方法为了什么?
参考答案
- 擦除与数组可具体化冲突,类型安全难保证。
- 在擦除后仍保持覆盖与调用的二进制兼容/多态分派。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Type Erasure Tutorial | 教程 | 擦除 |
| JLS 4.6 Type Erasure | 规范 | 规则 |