Java决定使用泛型类型参数调用哪个方法?

Java决定使用泛型类型参数调用哪个方法?,java,generics,Java,Generics,据我所知,Java在运行时删除泛型类型参数信息。它仅用于编译以执行检查,例如,此特定方法调用是否有效 今天我遇到了以下代码段,其中Java似乎通过collection/list类型参数确定要调用的构造函数: public static class MyClass { public MyClass(final Collection<String> coll) { System.out.println("Constructor 1"); } pub

据我所知,Java在运行时删除泛型类型参数信息。它仅用于编译以执行检查,例如,此特定方法调用是否有效

今天我遇到了以下代码段,其中Java似乎通过collection/list类型参数确定要调用的构造函数:

public static class MyClass {
    public MyClass(final Collection<String> coll) {
        System.out.println("Constructor 1");
    }
    public MyClass(final List<Integer> list) {
        System.out.println("Constructor 2");
    }
}
…这些电话的作用和我所期望的一样;使用列表参数的构造函数调用适用于“最精确”满足其需求的构造函数:

newmyclass2(newhashset());//构造器1
新建MyClass2(新建ArrayList());//建造师2
新建MyClass2(新建ArrayList());//建造师2

泛型信息似乎存储在已编译类(在本例中为MyClass)中,并且最终不会被丢弃,但它应该被丢弃。我误解了什么?

这里发生的事情是,编译器可以通过使用泛型来区分这两个构造函数,所以它在创建字节码和剥离泛型之前都是这样做的

在中间的情况下,它将告诉VM调用
MyClass2(集合)
(即生成与此特定构造函数匹配的字节码)

VM不会在运行时尝试确定匹配的方法。那太慢了。相反,它依赖于编译器创建非常特定的指令

这就是为什么即使通用信息在运行时被删除,上面的代码仍然可以工作的原因

[编辑]澄清一下:字节码包含编译器可以看到和使用的附加信息。你可以通过反射得到同样的信息

擦除意味着字节码解释器和JIT不关心泛型,这就是为什么不能将
setFoo(List)
setFoo(List)
放在同一个类中的原因:虽然编译器可以区分两者,但运行时不能

具体来说,当您通过反射检查方法时,您将获得泛型信息,但字节码解释器/JIT不使用反射。相反,它使用压缩的方法签名,该签名的内容类似于
methodcom/pany/Type/setFoo(Ljava.util.List;)V
——这里不再有泛型

相关的:


简单来说,编译器选择匹配的最窄类型


在原始参数版本中,选择什么是显而易见的,但在通用版本中,集合/列表的类型包含在匹配信息中:ArrayList既是集合又是列表,但是您可以看到,通过比较ArrayList的类型,很容易打破这种关系——现在只有一个方法仍然匹配。

没有注意到您正在处理构造函数。无论如何,下面的参数即使对于构造函数也是有效的


重载方法的方法调用由编译器在编译时解析。泛型仅在编译时用于类型检查。所以,这与类型擦除无关,类型擦除完全是一项运行时业务

考虑你的第一个案例: 现在,
ArrayList
List
Collection
的子类型。但是
列表
集合
更特定于
阵列列表
。因此,编译器将绑定方法调用:

new MyClass2(new ArrayList<String>());
newmyclass2(newarraylist());
列表
为参数的


参考文献:


+1问得好。我不清楚为什么会这样,但我很高兴看到一个很好的答案。我想这可能与Java在编译时选择要使用的方法有关。在编译时,编译器从一组重载方法中选择“最接近的匹配”。泛型参数会影响编译器进行选择时使用的算法。我将MyClass和MyClass2分离成一个单独的类,并对其进行编译。然后将main方法放入一个单独的类中,编译并调用它。结果是一样的。我假设存储了某种泛型类型参数信息,我将代码与类和main方法分为两个单独的类。用类编译类,用main方法编译类。使用main方法调用该类。但结果是一样的。这意味着泛型类型信息存储在第一个类中,然后在编译第二个类时使用?编译的
。class
文件保留其所有泛型信息。关于类定义的一般信息将永远保留在程序中;泛型类型信息仅在这些泛型类型的对象上丢失;e、 g.
列表
只会知道它是一个
列表
@SergejsPogorelovs:编译器和运行时使用不同的信息源。请看我的编辑。谢谢。我的代码指定了它使用的泛型,并且在编译代码中也指定了它,构造函数有哪些泛型。因此,代码理解了它应该调用哪个构造函数。我认为困扰我的是,如果在运行时丢弃了泛型信息,那么为什么在编译代码时要存储它呢?因此,可能在类编译后就丢弃了泛型信息。但是我错了,因为将来编译其他类仍然需要它,这将取决于我们的类和泛型。所以我假设泛型信息总是存储在字节码中,但在运行时它仍然被丢弃?在没有加载详细信息的情况下,某些代码是否希望调用参数中包含泛型的方法?
public static class MyClass2 {
    public MyClass2(final Collection coll) {
        System.out.println("Constructor 1");
    }
    public MyClass2(final List list) {
        System.out.println("Constructor 2");
    }
}
new MyClass2(new HashSet<String>()); // Constructor 1
new MyClass2(new ArrayList<String>()); // Constructor 2
new MyClass2(new ArrayList<Integer>()); // Constructor 2
public MyClass(final Collection<String> coll)
public MyClass(final List<Integer> list)
new MyClass(new HashSet<String>()); // Constructor 1
new MyClass(new ArrayList<String>()); // Constructor 1
new MyClass(new ArrayList<Integer>()); // Constructor 2
public MyClass2(final Collection coll)
public MyClass2(final List list)
new MyClass2(new ArrayList<String>());