Java Generics Curiosity
У меня есть интерфейс A
, какой класс B
реализует.
Следующий универсальный метод работает
public static <T, U extends T> List<T> listFactory(Collection<U> source) {
return new ArrayList<T>(source);
}
но
public static <T> List<T> listFactory(Collection<? extends T> source) {
return new ArrayList<T>(source);
}
не (ошибка компиляции, несоответствие типов), когда я направляю вывод в
List<A> tester = listFactory(B.defaultCollectionFactory(3));
defaultCollectionFactory(int count)
статически обеспечивает коллекцию B
s, со схемой маркировки по умолчанию.
Любое понимание того, почему это так? Кажется, что универсальный U и подстановочный знак делают то же самое.
2 ответа
В первой конструкции вы указываете, что возвращаете List
интерфейса переданного элемента. Вы указываете связь между переданным в Object и возвращаемым типом Object в U extends T
направление. В этом случае компилятор может ассоциировать A
а также B
с T
а такжеU
соответственно.
Во втором случае такой дифференциации нет, поэтому компилятор предполагает, что T
относится к B
и введите возвращаемое значение как List<B>
, Затем вы попадаете в ловушку, где, хотя B
это пример A
, List<B>
не является примером List<A>
, Компилятор будет жаловаться:
Несоответствие типов: невозможно преобразовать из списка в список
Вы обнаружите, что с первой конструкцией вы можете указать List
любого интерфейса B
реализует или любой суперкласс в B
иерархия (List<Object>
, например), и компилятор не будет жаловаться.
Компилятор выводит другой тип параметра для listFactory
метод, чем вы ожидаете. Это означает, что T
это тип B
Таким образом, подпись эффективно List<B> listFactory(Collection<? extends B> source)
, Укажите параметр типа A
будучи явным в вызове метода:
List<A> tester = Test.<A> listFactory(B.defaultCollectionFactory(3));