这是一个代码段
public class HelloWorld{
public static void main(String[] args) {
GenericClass<Integer> test = new GenericClass<Integer>();
test.method(new ArrayList<Integer> ());
}
}
class GenericClass<T> {
public void overloadedMethod( Collection<?> o) {
System.out.println("overloadedMethod(Collection<?>)");
}
public void overloadedMethod( List<Number> s) {
System.out.println("overloadedMethod(List<Number>)");
}
public void overloadedMethod( ArrayList<Integer> i) {
System.out.println("overloadedMethod(ArrayList<Integer>)");
}
public void method(List<T> t) {
overloadedMethod(t) ; // which method is called?
}
}
我期待它会用Arraylist参数调用重载方法,但是为什么会调用Collection?
答案 0 :(得分:5)
此精确代码用于Angelika Langer's webpage on java generics
中的示例如果页面出现故障,我会发布解释:
程序打印:
overloadedMethod(Collection)
有人可能会预期会调用ArrayList的版本,但这又是错误的期望。让我们看看编译器将泛型类转换为什么。
示例(在类型擦除之后):
{"id":"1","name":"joe"}
有人可能会错误地认为编译器会决定重载方法的List版本是最匹配的。但这当然是错的。重载方法的List版本最初是一个以List作为参数的版本,但在调用时会传递List,其中T可以是任何类型,不必是Number。由于T可以是任何类型,因此重载方法的唯一可行版本是Collection的版本。
事实上,如果你注释掉Collection方法,你会得到编译器错误,因为List和ArrayList太具体,不适合所有可能的public final class GenericClass {
private void overloadedMethod( Collection o) {
System.out.println("overloadedMethod(Collection<?>)");
}
private void overloadedMethod( List s) {
System.out.println("overloadedMethod(List<Number>)");
}
private void overloadedMethod( ArrayList i) {
System.out.println("overloadedMethod(ArrayList<Integer>)");
}
private void method(List t) {
overloadedMethod(t) ;
}
public static void main(String[] args) {
GenericClass test = new GenericClass();
test.method(new ArrayList ());
}
}
类型。
答案 1 :(得分:4)
请参阅标题为“方法调用表达式”的Java documention的第15.12节。更具体地说,第15.12.2.5节选择最具体的方法:
如果多个成员方法都可访问且适用于某个方法 调用时,有必要选择一个为运行时提供描述符 方法调度。 Java编程语言使用最多的规则 选择具体方法。
非正式的直觉是一种方法更具体 如果可以传递第一个方法处理的任何调用,则为另一个 没有编译时错误的另一个。在诸如此类的情况下 显式类型化的lambda表达式参数(第15.27.1节)或变量 arity调用(§15.12.2.4),允许一些灵活性适应 一个签名到另一个。
此外,第8.4.9节称为重载状态:
调用方法时(第15.12节),实际参数的数量(以及任何参数) 使用显式类型参数)和参数的编译时类型, 在编译时,确定将被调用的方法的签名 (§15.12.2)。如果要调用的方法是实例方法,则为实际方法 将在运行时使用动态方法查找确定要调用的方法 (§15.12.4)。
考虑到这两个部分,我们可以确定,因为method
传递了一个List<T>
对象,所以Java找到的最具体的目标是通配符Collections
。
请注意,即使您已将参数更改为ArrayList<T>
,也是如此:
public void method(ArrayList<T> t) {
overloadedMethod(t) ; // which method is called?
}
仍然会调用相同的方法,因为接受ArrayList的特定重载太具体了 - ArrayList<Integer>
与ArrayList<T>
的参数相对应,因此Java确定Collection<?>
为最佳匹配(您当然可以通过更改method
的参数类型或使overloadedMethod
的参数更具体来规避它。