为什么通用版本的Collection参数正在调用

时间:2015-08-23 04:42:16

标签: java generics

这是一个代码段

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?

2 个答案:

答案 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的参数更具体来规避它。