Как привести список наследуемых объектов к коллекции объектов в Java? - PullRequest
11 голосов
/ 03 мая 2010

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

Collection<A> collecA

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

List<B> listB

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

class B extends A { ... }

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

collecA = listB

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

Ответы [ 4 ]

16 голосов
/ 03 мая 2010

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

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 с. Однако, если вышеупомянутое назначение было разрешено, то у нас есть проблема, потому что 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 с. Однако экземпляр List<B> не может содержать A с. Следовательно, переменной List<A> , такой как listA, нельзя присвоить ссылку на List<B> экземпляр , на который ссылается listB.

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


Именно этот пример / аналогия из Общего руководства по Java помог мне понять это:

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 = ...;

Но как насчет "клетки для животных"? Английский язык неоднозначен, поэтому, если быть точным, давайте предположим, что мы говорим о «клетке для животных» :

Cage<Animal> animalCage = ...;

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

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

"

9 голосов
/ 03 мая 2010
Collection<? extends A> collecA

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

5 голосов
/ 03 мая 2010

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

См. Теория и практика Java: Generics Gotchas для получения дополнительной информации.

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

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

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!
1 голос
/ 03 мая 2010

Вы можете назначить Список для Коллекции , но не Список для Коллекции .

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

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

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

Поэтому было решено, что List - это Collection , но НЕ List (илиКоллекция ).

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

...