Дженерики в переопределенных методах
столкнулся с интересной проблемой; следующий класс составляет:
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()
нет