Java为何不会擦除继承链中的泛型类型?
为什么继承泛型类型并指定具体类型时,类型不会被擦除?
Java的泛型类型擦除大多发生在泛型实例化场景,但有个特殊情况:当你继承泛型类型并把泛型参数<T>替换成具体类型时,这个具体类型在运行时是可通过反射获取的,这几乎是类型不会被完全擦除的唯一场景。
示例1:普通泛型实例化,类型会被擦除
List<String> text = new ArrayList<>();
这里的String类型会被擦除,运行时JVM只知道text是一个List实例,无法直接获取到它的元素类型是String。
示例2:泛型类本身的实现,类型会被擦除
public final class ArrayList<T> implements List<T> { ... }
因为ArrayList本身是泛型类,不管它被实例化为哪种具体类型,泛型参数T对应的实际类型都会在编译后被擦除,运行时无法通过实例获取到T的具体类型。
示例3:继承泛型并指定具体类型,类型可被反射获取
public final class IntegerList implements List<Integer> { ... }
此时IntegerList不是泛型类,它实现的List<Integer>中的Integer会被保留在类的字节码元数据里,通过反射API(比如getGenericInterfaces())就能读取到这个具体类型。
核心原因
Java的类型擦除机制是为了兼容泛型出现前的旧代码,但JVM需要保留部分泛型元数据来支持反射、序列化等功能。当非泛型类继承/实现带有具体类型参数的泛型父类/接口时,这个具体类型参数会被写入类的Signature属性(字节码中的特殊元数据)中。
由于这个子类本身没有泛型参数,不存在需要擦除的类型变量,JVM可以安全地把这个具体类型信息保留下来,不会像泛型实例化或泛型类定义那样擦除类型参数。而泛型实例化时,变量的泛型信息仅在编译期用于类型检查,运行时实例本身还是原始类型;泛型类定义中的T则会被擦除为其边界类型(比如Object),所以运行时也无法获取到具体的类型参数。
内容的提问来源于stack exchange,提问作者Matthew Layton
相关产品推荐
相关产品推荐

