泛型与类型擦除
2026/9/14大约 2 分钟
泛型与类型擦除
Java 的泛型是"伪泛型",编译后会被类型擦除替换为原生类型并插入强制转型代码。
什么是类型擦除
Java 语言在 JDK 1.5 之后引入的泛型实际上只在程序源码中存在。编译后的字节码文件中,泛型已被替换为原来的原生类型,并在相应位置插入了强制转型代码。
因此对运行期来说,ArrayList<String> 和 ArrayList<Integer> 是同一个类。
泛型技术是 Java 语言的语法糖,基于类型擦除实现的泛型被称为伪泛型。
反编译示例
泛型擦除前后的源码示例代码如下:
Java
// 源码
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "No.1");
System.out.println(map.get(1));Kotlin
// 源码(Kotlin 的泛型是真实泛型,运行期保留类型信息)
val map = HashMap<Int, String>()
map.put(1, "No.1")
println(map.get(1))反编译后的字节码示例代码如下:
Java
// 编译后(反编译结果)
Map map = new HashMap();
map.put(1, "No.1");
System.out.println((String) map.get(1)); // 插入强制转型Kotlin
// 编译后(反编译结果,Kotlin 中对应未指定泛型的情况)
val map = HashMap<Any, Any>()
map.put(1, "No.1")
println(map.get(1) as String) // 需要强制转型泛型都变回了原生类型,且 get() 返回处插入了强制转型代码。
类型擦除的后果
方法重载冲突:以下两个方法编译时发生名称冲突——因为擦除后它们的签名完全相同(都是 method(List)):
Java
public void method(List<String> list) { } // 冲突
public void method(List<Integer> list) { } // 冲突:具有相同擦除签名Kotlin
// Kotlin 中同样存在 JVM 擦除签名冲突,需用 @JvmName 区分
@JvmName("methodString")
fun method(list: List<String>) { }
@JvmName("methodInt")
fun method(list: List<Int>) { }为什么这样设计
- 兼容性:类型擦除让新泛型代码能与 JDK 1.5 之前的旧代码(无泛型)共存
- 代价是丢失了运行期的类型信息(可通过反射获取部分泛型信息,如
ParameterizedType)