Как передать список наследуемых объектов в коллекцию объектов в Java?

У меня есть тип коллекции:

Collection<A> collecA

И у меня есть список в моем объекте:

List<B> listB

Где B расширяет A

class B extends A { ... }

Но я не могу сделать следующее:

collecA = listB

Я не понимаю, почему, поскольку Collection реализуется List.

Ответ 1

Предположим на мгновение вы могли бы сделать то, что вы описали:

class B extends A { ... }

Collection<A> collecA;
List<B> listB;

collecA = listB;  // normally an error, but lets pretend its allowed

collecA.add(new A()); // PROBLEM!

Вызов метода collecA.add(new A()) выглядит нормально, так как collecA представляет собой набор которая содержит A s. Однако, если указанное присвоение было разрешено, то мы имеем потому что collecA действительно ссылается на экземпляр List<B> - я просто добавлен A в список, который может содержать только B s!

Аскер также сказал:

Я не понимаю, почему, поскольку Collection реализуется List.

Не имеет значения, что Collection является суперклассом List. Это назначение является незаконным, даже если вы использовали два списка.

class B extends A { ... }
List<A> listA;
List<B> listB;
listA = listB;  // still an error, still leads to the same problem

Ключ состоит в том, что переменная List<A> может ссылаться только на List, которая может содержать A s. Однако экземпляр List<B> не может содержать A s. Поэтому переменной List<A>, подобной listA, не может быть присвоена ссылка на экземпляр List<B>, на который ссылается listB.

Или, вообще говоря, B, являющийся подклассом A, имеет не, что SomeGenericClass<B> является подклассом SomeGenericClass<A> (JLS §4.10: подтипирование не распространяется на общие типы: T <: U не означает, что C<T> <: C<U>.)


Именно этот пример/аналогия из учебника Java Generics помог мне понять это:

http://java.sun.com/docs/books/tutorial/java/generics/subtyping.html

"Понимание того, почему становится намного проще, если вы думаете о материальных объектах - вещи, которые вы можете реально изобразить, например, клетка:

// A cage is a collection of things, with bars to keep them in.
interface Cage<E> extends Collection<E>;
...
Cage<Lion> lionCage = ...;
Cage<Butterfly> butterflyCage = ...;

Но как насчет "клетки животных"? Английский двусмыслен, поэтому, если быть точным, предположим, что мы говорим о клетке "all-animal cage" :

Cage<Animal> animalCage = ...;

Это клетка, предназначенная для хранения всех видов животных, смешанных. У него должны быть решетки, достаточно сильные, чтобы держаться у львов, и располагаться достаточно близко, чтобы удержать бабочек. ...
Поскольку лев - это своего рода животное (Лев - подтип животного), тогда возникает вопрос: "Является ли клетка льва своего рода клеткой для животных?" Cage<Lion> подтип Cage<Animal>? ". Согласно приведенному выше определению клетки для животных, ответ должен быть" нет ". Это удивительно! Но это имеет смысл, когда вы думаете об этом: клетку-лев нельзя считать бабочкой, и клетку бабочки нельзя считать удерживающей во львах. Поэтому ни клетка не может считаться клеткой" все животные":

animalCage = lionCage;  // compile-time error
animalCage = butterflyCage; // compile-time error

"

Ответ 2

Collection<? extends A> collecA

Это исправляет это. Проблема заключается не в List extends Collection, а в общих типах.

Ответ 3

Генераторы Java не являются ковариантными.

Подробнее см. Теория и практика Java: Generics Gotchas.

На странице показан простой пример, который мог бы испортить систему типов, если она была ковариантной:

Представьте, что вы можете назначить List <Integer> в List <Number> . Затем следующий код позволит вам помещать нечто, не являющееся целым, в List <Integer> :

List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415)); // ERROR: Adds a float to li, which is a list of Integers!

Ответ 4

Вы можете назначить List <B> в Collection <B> , но не List <B> в Collection <A> .

Представьте, что произойдет, если это будет возможно:

List<B> = new ArrayList<B>();
Collection<A> collecA = listB; //Assume that this line compiles
collecA.add(new A());
B item = listB.get(0); //ClassCastException!

Как вы видите, мы "обманули" систему типов generics, добавив экземпляр конкретного типа A в коллекцию, которая должна была иметь только объекты типа B (или потомки). Как следствие, последняя строка, которая выполняет неявное преобразование в B, терпит неудачу с ClassCastException. Что с этим не так? Компилятор не может гарантировать безопасность типа, а также один из принципов генерирования Java.

Следовательно, было решено, что List <B> является Collection <B> , но NOT List <A> (или Collection <A> ).

В качестве побочного комментария интересно отметить, что массивы не соответствуют тем же правилам: String [] - объект [], а присвоения являются законными.