14

遇到一个有趣的问题;以下类编译:

public class Test {

    public static void main(String[] args) throws Exception {
        A a = new A();
        B b = new B();

        foo(a);
        foo(b);
    }   

    private static void foo(A a) {
        System.out.println("In A");
    }   

    private static void foo(B b) {
        System.out.println("In B");
    }   

    private static class A {}

    private static class B extends A {}

}

但是这个失败了:

public class Test {

    public static void main(String[] args) throws Exception {
        A<String> a = new A<>();
        B b = new B();

        foo(a);
        foo(b);
    }   

    private static void foo(A<String> a) {
        System.out.println("In A");
    }   

    private static void foo(B b) {
        System.out.println("In B");
    }   

    private static class A<T> {}

    private static class B extends A {}

}

出现此错误:

Test.java:8: error: reference to foo is ambiguous, both method foo(A<String>) in Test and method foo(B) in Test match              
        foo(b);                                                                                                                    
        ^                                                                                                                          
Note: Test.java uses unchecked or unsafe operations.                                                                               
Note: Recompile with -Xlint:unchecked for details.                                                                                 
1 error

我原以为由于类型擦除,这些基本上是相同的。有人知道这里发生了什么吗?

4

3 回答 3

3

原因是您混合了泛型和原始类型( B 应该声明为class B<T> extends A<T>or class B extends A<SomeType>)。

发生这种情况的实际原因被埋在JLS 的某个地方,第 #15.12.2.7 节及以下 - 祝你好运,简洁地表达它;-)

于 2013-04-11T20:01:30.023 回答
2

在泛型出现之前,Java 有类似的方法

public class Collections

    public void sort(List list) {...}               [1]

用户代码可能有类似的东西

public class MyList implements List ...             [2]

MyList myList = ...;
Collections.sort(myList);                           [3]

当泛型添加到 Java 中时,决定将现有的类和方法转换为泛型,而不会破坏使用它们的任何代码。就难度而言,这是一项了不起的成就,但代价是让语言变得复杂和有缺陷。

所以[1]被泛化,但[3]仍必须按原样编译,而不必泛化[2].

黑客在§15.12.2.3

可以通过方法调用转换(第 5.3 节)将 Ai 转换为 Si

基本上说如果参数类型(Ai)是原始的,那么为了匹配的目的也删除参数类型(Si)。

回到你的例子,我们看到为什么foo(A<String>)被认为适用于foo(b).

但是还有另一个问题 - 是否foo(A<String>)适用于 [§15.12.2.2]?从规范的字母来看,答案似乎是“不”。但这可能是规范的错误。

于 2013-04-11T21:29:06.203 回答
1
private static class B extends A {}

您在A这里省略了类型参数。你可能的意思是

private static class B<T> extends A<T> {}

此外,B b = new B()也没有<String>论据;你必须做

B<String> b = new B<String>();

……最后,

private static void foo(B b) {
    System.out.println("In B");
} 

应该是这样的

private static void foo(B<String> b) {
    System.out.println("In B");
} 

一般来说,如果有一个类型Foo有一个泛型参数,Foo那么基本上应该总是有一个类型参数。在这种特殊情况下,class B extends A没有类型参数 for A,然后B需要一个类型参数,因此您在提到的任何地方都需要一个类型参数B。(该规则的一个主要例外是instanceof表达式,但这里没有。)

于 2013-04-11T20:01:09.113 回答