注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过
- Java泛型1——概述
- Java泛型2——泛型类
- Java泛型3——泛型接口
- Java泛型4——泛型方法
- Java泛型5——泛型通配符
- Java泛型6——类型擦除
1. 什么是类型擦除
泛型是在Java 1.5被引进的,但是泛型代码可以和之前的版本兼容,原因就是泛型只存在代码编译阶段,编译后的字节码中(也就是运行时)不包含泛型中的类型信息。使用泛型时,泛型相关的类型参数信息会在编译时被擦除掉,这个过程被称为类型擦除。比如:
import java.util.LinkedList;
public class Main {
public static void main(String[] args) {
LinkedList<Integer> i = new LinkedList<>();
LinkedList<String> s = new LinkedList<>();
System.out.println("i:" + i.getClass());
System.out.println("s:" + s.getClass());
}
}
从运行结果可以看到,虽然为 i
和 s
指定了不同的类型参数,但在运行时它们实际上是同一种类型(都是 LinkedList
)。这就是类型擦除的作用,在编译时将代码中的类型参数擦除只保留原始类型。
2. 原始类型
原始类型是擦除了泛型信息后字节码中的类型,无论何时定义一个泛型类型,相应的原始类型都会被自动地提供。类型参数信息被擦除只会保留原始类型。根据是否限定类型,原始类型替换有以下两种情况:
2.1 无限定类型
将类型参数替换为Object
。例如:
class Test<T> {
private T value;
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
}
类型擦除后:
class Test {
private Object value;
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
2.2 有限定类型
将类型参数替换为上限或下限。例如:
class Test<T extends Number> {
private T value;
}
类型擦除后:
class Test {
private Number value;
}
3. 类型擦除原理
Java在编译时进行泛型的类型擦除,但在进行类型擦除时会将传入的类型参数记录下来。当对被擦除后的泛型对象操作时编译器会根据记录的类型参数对其进行类型转换,将其从原始类型转换为未传入的参数类型。
4. 类型擦除带来的问题
主要有以下几点(部分问题在之前已经说过了,如泛型变量不能是基本数据类型,具体内容可参考CSDN博客):
- 引用传递问题
- 自动类型转换问题
- 类型擦除与多态冲突
- 泛型变量不能是基本数据类型
- 运行时类型检查异常
- 泛型异常捕获
- 不能创建泛型类型数组
- 不能实例化泛型类型
- 类型擦除后的冲突
- 不能使用静态域