Дженерики в переопределенных методах

столкнулся с интересной проблемой; следующий класс составляет:

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

Я бы подумал, что из-за стирания типа они будут по существу идентичны. Кто-нибудь знает, что здесь происходит?

3 ответов


до рассвета дженериков 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] должны по-прежнему компилироваться как есть, без generify [2].

взлом в §15.12.2.3

Ai может быть преобразован методом преобразования вызова (§5.3) в Si

в основном говоря, что если тип аргумента (Ai) является raw, то удалите тип параметра (Si) тоже с целью сопоставления.

вернуться к вашему примеру, мы видим, почему foo(A<String>) считается применимым для foo(b).

однако есть другой вопрос - это foo(A<String>) применимо для [§15.12.2.2]? Ответ, по-видимому," нет " по букве спецификации. Но это может быть ошибка спецификации.


причина в том, что вы смешиваете дженерики и сырые типы (B должен быть объявлен как class B<T> extends A<T> или class B extends A<SomeType>).

фактическая причина, по которой это происходит, похоронена где-то в JLS, раздел #15.12.2.7 и следующие - удачи, чтобы сформулировать его кратко ; -)


private static class B extends A {}

вы опускаете аргументы типа для A здесь. То, что вы, вероятно, имеете в виду

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

кроме того, B b = new B() нет