Java 泛型类型擦除:何时发生以及会发生什么?

2022-08-31 05:23:19

我在Oracle的网站上读到了Java的类型擦除。

类型擦除何时发生?在编译时还是在运行时?何时加载类?何时实例化类?

许多网站(包括上面提到的官方教程)都说类型擦除发生在编译时。如果在编译时完全删除了类型信息,那么当调用使用泛型的方法而没有类型信息或错误的类型信息时,JDK 如何检查类型兼容性?

请考虑以下示例:假设类有一个方法 。我们编译并获取类文件 。Aempty(Box<? extends Number> b)A.javaA.class

public class A {
    public static void empty(Box<? extends Number> b) {}
}
public class Box<T> {}

现在我们创建另一个类,该类使用非参数化参数(原始类型)调用方法:。如果我们在类路径中使用编译,javac 足够聪明,可以发出警告。因此,其中存储了一些类型信息。Bemptyempty(new Box())B.javaA.classA.class

public class B {
    public static void invoke() {
        // java: unchecked method invocation:
        //  method empty in class A is applied to given types
        //  required: Box<? extends java.lang.Number>
        //  found:    Box
        // java: unchecked conversion
        //  required: Box<? extends java.lang.Number>
        //  found:    Box
        A.empty(new Box());
    }
}

我的猜测是,类型擦除发生在加载类时,但这只是一个猜测。那么它什么时候会发生呢?


答案 1

类型擦除适用于泛型的使用。类文件中肯定有元数据来说明方法/类型是否是泛型的,以及约束是什么等。但是,当使用泛型时,它们被转换为编译时检查和执行时强制转换。所以这个代码:

List<String> list = new ArrayList<String>();
list.add("Hi");
String x = list.get(0);

被编译成

List list = new ArrayList();
list.add("Hi");
String x = (String) list.get(0);

在执行时,无法发现列表对象的信息消失了。T=String

...但是接口本身仍然将自己宣传为通用的。List<T>

编辑:只是为了澄清,编译器确实保留了有关变量的信息 - 但是对于列表对象本身,您仍然无法找到。List<String>T=String


答案 2

编译器负责在编译时理解泛型。编译器还负责抛弃对泛型类的这种“理解”,在这个过程中,我们称之为类型擦除。一切都发生在编译时。

注意:与大多数Java开发人员的信念相反,尽管以非常有限的方式,但可以保留编译时类型信息并在运行时检索此信息。换句话说:Java确实以一种非常受限制的方式提供了重新化的泛型

关于类型擦除

请注意,在编译时,编译器具有可用的完整类型信息,但在生成字节码时,此信息通常会在称为类型擦除的过程中有意删除。由于兼容性问题,这是以这种方式完成的:语言设计人员的意图是在平台版本之间提供完整的源代码兼容性和完整的字节码兼容性。如果以不同的方式实现它,则在迁移到较新版本的平台时,必须重新编译旧版应用程序。按照它完成的方式,所有方法签名都保留(源代码兼容性),您不需要重新编译任何内容(二进制兼容性)。

关于 Java 中的已初始化泛型

如果需要保留编译时类型信息,则需要使用匿名类。关键是:在匿名类的非常特殊的情况下,可以在运行时检索完整的编译时类型信息,换句话说,这意味着:重新化的泛型。这意味着编译器在涉及匿名类时不会丢弃类型信息;此信息保存在生成的二进制代码中,运行时系统允许您检索此信息。

我写了一篇关于这个主题的文章:

https://rgomes.info/using-typetokens-to-retrieve-generic-parameters/

关于上述文章中描述的技术的说明是,该技术对于大多数开发人员来说是晦涩难懂的。尽管它工作得很好,但大多数开发人员对这项技术感到困惑或不舒服。如果您有共享代码库或计划向公众发布代码,我不建议使用上述技术。另一方面,如果您是代码的唯一用户,则可以利用此技术为您提供的强大功能。

示例代码

上面的文章包含指向示例代码的链接。


推荐