什么是类型擦除?
面试回答¶
面试官: 什么是类型擦除?
你:
类型擦除就是Java泛型在编译期把类型参数抹掉,替换成原始类型或上界类型,并在使用的地方插入强制类型转换。说白了,泛型信息只活在编译阶段,字节码里没有,运行时压根不知道你写的是List<String>还是List<Integer>。
为什么要擦除?
为了向后兼容。Java5引入泛型时,已经有海量的老代码在用List(原生类型)。如果把泛型带到运行期,所有老类库都得重写。擦除让泛型可以和原生类型混用,旧代码不用改就能跑在新版JVM上。
擦除规则:
-
无限制类型参数
T→ 擦成Object。 -
有上界
T extends Comparable<T>→ 擦成Comparable。 -
泛型类中的字段和方法,类型参数在字节码层面就是擦除后的类型。
-
调用泛型方法时,编译器自动插入
checkcast指令,把取出来的Object强转回具体类型。这也是为什么你写String s = list.get(0)不用自己强转,但字节码里实际有强转。 -
桥方法:为了实现多态,编译器可能会生成额外的桥接方法,保证擦除后仍然能正确重写。
擦除带来的问题:
-
运行期拿不到泛型类型。你没法判断一个
List到底是List<String>还是List<Integer>,只能用instanceof判断原生类型。 -
不能创建泛型数组。
new T[10]编译报错,因为运行期不知道 T 是啥。 -
方法签名冲突。比如
void add(List<String>)和void add(List<Integer>)擦除后都是void add(List),不能重载。 -
基本类型不能用。只能写
List<Integer>,因为擦除后需要强转Object,基本类型没法放到Object里,需要自动装箱。
时序图:编译器抹掉泛型

一句话:泛型是编译器给的语法糖,运行时全是原生类型加一堆强制转换。