重写方法中的泛型

2022-09-04 04:26:31

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

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

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


答案 1

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

发生这种情况的真正原因被埋葬在JLS的某个地方,#15.12.2.7节及以下 - 祝你好运,简洁地阐明它;-)


答案 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

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

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

回到您的示例,我们了解为什么认为适用于 。foo(A<String>)foo(b)

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