Я играл с дженериками и обнаружил, что, к моему удивлению, следующий код компилируется:
class A {}
class B extends A {}
class Generic<T> {
private T instance;
public Generic(T instance) {
this.instance = instance;
}
public T get(){ return instance; }
}
public class Main {
public static void main(String[] args) {
fArray(new B[1], new Generic<A>(new A())); // <-- No error here
}
public static <T> void fArray(T[] a, Generic<? extends T> b) {
a[0] = b.get();
}
}
Я ожидал бы, что T
будет выведен на B
. A
не распространяется B
. Так почему компилятор не жалуется на это?
T
, как представляется, выводится на Object
, так как я могу передать a Generic<Object>
.
Кроме того, при фактическом запуске кода он выдает ArrayStoreException
в строке a[0] = b.get();
.
Я не использую сырые общие типы. Я чувствую, что этого исключения можно было избежать с ошибкой времени компиляции или, по крайней мере, предупреждением, если T
было фактически выведено как B
.
При дальнейшем тестировании с эквивалентом List<...>
:
public static void main(String[] args) {
fList(new ArrayList<B>(), new Generic<A>(new A())); // <-- Error, as expected
}
public static <T> void fList(List<T> a, Generic<? extends T> b) {
a.add(b.get());
}
Это приводит к ошибкам:
The method fList(List<T>, Generic<? extends T>) in the type Main is not applicable for the arguments (ArrayList<B>, Generic<A>)
Как и более общий случай:
public static <T> void fList(List<? extends T> a, Generic<? extends T> b) {
a.add(b.get()); // <-- Error here
}
Компилятор правильно распознает, что первый ?
может быть ниже иерархии наследования, чем второй ?
.
например. Если первый ?
был B
, а второй ?
был A
, тогда это не безопасно для типа.
Так почему же первый пример не создает аналогичную ошибку компилятора? Это просто надзор? Или существует техническое ограничение?
Единственный способ, которым я мог создать ошибку, - это явно указать тип:
Main.<B>fArray(new B[1], new Generic<A>(new A())); // <-- Not applicable
Я действительно ничего не нашел в своих исследованиях, кроме этой статьи с 2005 года (до дженериков), в котором говорится о опасности ковариации массива.
Ковариация массивов, похоже, намекает на объяснение, но я не могу думать об этом.
Текущий jdk равен 1.8.0.0_91