Java决定使用泛型类型参数调用哪个方法?
据我所知,Java在运行时删除泛型类型参数信息。它仅用于编译以执行检查,例如,此特定方法调用是否有效 今天我遇到了以下代码段,其中Java似乎通过collection/list类型参数确定要调用的构造函数: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
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());
以列表
为参数的
参考文献:
。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>());