泛型看似强大的类型机制,其实只存在于编译期。
在运行时,它们都被“擦除”成原始类型(Raw Type)。
本文将深入讲解泛型类型擦除的原理、表现与影响。


一、什么是类型擦除(Type Erasure)

类型擦除 是 Java 实现泛型的核心机制。
泛型信息只存在于 编译阶段,编译完成后,所有类型参数都会被擦除。

List<String> list1 = new ArrayList<>();
List<Integer> list2 = new ArrayList<>();

System.out.println(list1.getClass() == list2.getClass()); // true、

输出:

true

💡 编译器认为两者的运行时类型完全相同:ArrayList


二、为什么 Java 要类型擦除?

历史原因:为了与 Java 1.4 以前的代码兼容
JVM 层面并没有真正的泛型类型支持,因此泛型由编译器“伪装”实现。

编译器行为:

  1. 编译时检查泛型类型安全;
  2. 生成字节码时擦除泛型信息;
  3. 自动插入强制类型转换。

三、类型擦除的过程

1️⃣ 无边界类型擦除

class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

擦除后变成:

class Box {
    private Object value;
    public void set(Object value) { this.value = value; }
    public Object get() { return value; }
}

2️⃣ 有上界类型擦除

class Calculator<T extends Number> {
    public void show(T t) {
        System.out.println(t.doubleValue());
    }
}

擦除后:

class Calculator {
    public void show(Number t) {
        System.out.println(t.doubleValue());
    }
}

四、桥方法(Bridge Method)

泛型擦除可能导致 方法签名冲突
JVM 通过自动生成“桥方法”保证多态正确性。

示例:

class Parent<T> {
    T getValue() { return null; }
}
class Child extends Parent<String> {
    @Override
    String getValue() { return "Child"; }
}

编译后,Child 类会生成一个桥方法

// 自动生成
Object getValue() { return getValue(); }

⚙️ 这就是为什么泛型方法仍然能正确覆写的原因。


五、泛型擦除导致的限制

限制 原因
❌ 无法创建泛型数组 new T[10] 在运行时无法确定类型
❌ 无法使用 instanceof 检查泛型类型 if (obj instanceof List<String>) 无法通过编译
❌ 泛型类型无法直接获取 Class T.class 无法使用
❌ 静态成员无法使用泛型类型参数 泛型类型参数属于实例级别

六、通过反射获取泛型信息

虽然擦除了类型信息,但反射仍可读取部分泛型定义(存在于 class 元数据中)。

Field field = User.class.getDeclaredField("list");
Type type = field.getGenericType();
System.out.println(type);

输出:

java.util.List<java.lang.String>

七、类型擦除引发的陷阱

⚠️ 泛型与重载

void test(List<String> list) {}
void test(List<Integer> list) {} // 编译错误:擦除后方法签名相同

擦除后两者都变为:

void test(List list)

八、类型擦除与原始类型(Raw Type)

List raw = new ArrayList(); // 原始类型(不安全)
raw.add("A");
raw.add(1);

原始类型跳过编译期检查,可能导致运行时异常:

for (String s : raw) { // ClassCastException
    System.out.println(s);
}

九、JVM 层面:泛型的“假象”

泛型在 JVM 层面并不存在独立类型,JVM 只识别:

Ljava/util/List;

而不是:

Ljava/util/List<Ljava/lang/String;>;

泛型是 编译器的语法糖,运行时全部擦除为原始类型。


🔚 十、总结

概念 含义
类型擦除 编译阶段删除泛型信息
上界擦除 转换为限定类型,如 T extends NumberNumber
桥方法 为保持多态性自动生成的桥接函数
限制 无法实例化泛型数组、无法检查泛型类型
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐