Java集合框架中的泛型类型擦除(Type Erasure)是指编译器在编译阶段移除泛型类型信息,将泛型代码转换为原始类型(Raw Type)代码的过程。这一机制确保了泛型代码与Java早期版本的兼容性,同时通过编译期检查维持类型安全。以下是其具体工作方式:
1. 类型擦除的核心过程
编译时,泛型的类型参数会被替换为其边界类型(若无边界则替换为Object),所有泛型相关的类型信息(如List中的String)会被擦除,仅保留原始类型(如List)。
示例1:无边界泛型的擦除
// 泛型代码
List<String> list = new ArrayList<>();
list.add("hello");
String str = list.get(0);
// 擦除后等价于
List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0); // 编译器自动插入类型转换
复制
示例2:有边界泛型的擦除
// 泛型代码
class NumberList<T extends Number> {
private T value;
public T getValue() { return value; }
}
// 擦除后等价于
class NumberList {
private Number value; // T被替换为边界类型Number
public Number getValue() { return value; }
}
复制
2. 集合框架中类型擦除的具体表现
(1)集合类的擦除
所有泛型集合类(如ArrayList、HashMap<K,V>)在擦除后,类型参数会被替换为Object(或边界类型):
- ArrayList → 擦除为 ArrayList,内部存储的elementData数组类型为Object[]
- HashMap<K,V> → 擦除为 HashMap,键值对的类型均为Object
(2)方法的擦除与桥接方法
当泛型方法被擦除后,编译器会生成桥接方法(Bridge Method)以保证多态性和类型安全。
示例:
// 泛型接口
interface Comparable<T> {
int compareTo(T o);
}
// 实现类
class Integer implements Comparable<Integer> {
public int compareTo(Integer o) { ... }
}
复制
擦除后,Comparable变为Comparable,compareTo(T)变为compareTo(Object)。为了保持多态,编译器会为Integer生成桥接方法:
class Integer implements Comparable {
// 原始实现
public int compareTo(Integer o) { ... }
// 编译器生成的桥接方法
public int compareTo(Object o) {
return compareTo((Integer) o); // 强制转换,确保类型匹配
}
}
复制
桥接方法的作用是:当通过原始类型调用方法时(如Comparable.compareTo(Object)),会先进行类型转换,若类型不匹配则抛出ClassCastException。
(3)通配符的处理
泛型通配符(如? extends T、? super T)仅在编译期有效,擦除后会被替换为边界类型:
- List<? extends Number> → 擦除为 List
- List<? super Integer> → 擦除为 List
3. 类型擦除的限制(集合框架中的体现)
由于类型信息在运行时被擦除,导致以下限制:
- 无法直接实例化泛型类型
T obj = new T(); // 编译错误(擦除后T变为Object,无法确定具体类型)复制
集合框架中创建数组时需使用Object数组,如ArrayList的elementData为Object[]。
- 无法使用instanceof检查泛型类型
if (list instanceof List<String>) { ... } // 编译错误复制
运行时List和List的类型都是List,无法区分。
- 泛型数组创建受限
List<String>[] arr = new List<String>[10]; // 编译错误复制
擦除后变为List[],可能导致类型安全问题(如存入List)。
4. 类型擦除与类型安全的平衡
虽然类型信息在运行时被擦除,但编译器通过以下方式维持类型安全:
- 编译期检查:确保存入集合的元素类型与声明一致。
- 自动插入类型转换:从集合中取元素时,自动添加强制转换代码(如String str = (String) list.get(0))。
- 桥接方法:在运行时对擦除后的方法调用进行类型校验。
总结
泛型类型擦除是Java为兼容旧版本而设计的折中方案,其核心是:
- 编译期:保留泛型信息,用于类型检查和生成转换代码。
- 运行期:擦除类型参数,替换为边界类型(或Object),通过桥接方法保证多态性。
这一机制使得Java集合框架既能享受泛型带来的类型安全和代码简洁性,又能兼容没有泛型的早期版本。理解类型擦除有助于解释泛型的各种限制(如无法实例化T、无法创建泛型数组等)。