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

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

Collection<A> collecA

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

List<B> listB

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

class B extends A { ... }

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

collecA = listB

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

4 ответа

Решение

Давайте на минутку предположим, что вы можете сделать то, что описали:

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!

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

Я не могу понять, почему, так как коллекция реализована в 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 = ...;

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

Cage<Animal> animalCage = ...;

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

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

"

Collection<? extends A> collecA

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

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

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

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

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

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!

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

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

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.

Поэтому было решено, что список является коллекцией, но НЕ списком (или коллекцией).

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

Другие вопросы по тегам