У меня есть тип набора:
Collection<A> collecA
И у меня есть список в моем объекте:
List<B> listB
Где B расширяет A
class B extends A { ... }
Но я не могу сделать следующего:
collecA = listB
Я не могу понять, почему, так как Набор реализован Списком.
Предположим на мгновение, что вы можете сделать то, что описываете:
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
— я просто
добавил A
в список, который может содержать только B
!
Аскер также сказал:
Я не могу понять почему, так как Коллекция реализована Листом.
Неважно, что Collection является надклассом List. Это присвоение является недопустимым, даже если вы использовали два списка.
class B extends A { ... }
List<A> listA;
List<B> listB;
listA = listB; // still an error, still leads to the same problem
Ключ в том, что переменная List
может ссылаться только на List
, которые могут содержать A
. Однако экземпляр List
не может содержать A
s.Следовательно, List
переменная, такая как listA
, не может быть назначена ссылкой на List
экземпляр, на который ссылаетсясписокB
.
Или, в более общем смысле: B
, являющийся подклассом A
, не означает, что SomeGenericClass
является подклассом SomeGenericClass
(JLS §4.10: Подтипирование не распространяется на универсальные типы: T <: U
не означает, что C
.)
Этот пример/аналогия из Java Generics Tutorial помог мне понять это:
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
подтипом Клетка<Животное>
?". По приведенному выше определению клетки для животных ответ должен быть «нет».Это удивительно! Но это имеет смысл, если задуматься: нельзя предположить, что клетка со львом может содержать бабочек, а клетка с бабочкой не может содержать львов. Следовательно, ни одна из клеток не может считаться клеткой для животных:
animalCage = lionCage; // compile-time error
animalCage = butterflyCage; // compile-time error
"
Collection<? extends A> collecA
Это исправляет ситуацию. Проблема не в List extends Collection
, а в общих типах.
Java generics не являются ковариантными.
См. Теория и практика Java: Generics Gotchas для более подробной информации.
На странице показан простой пример, который мог бы разрушить систему типов, если бы она была ковариантной:
Представьте, что вы можете присвоить List
к List . Тогда следующий код позволит вам поместить в 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!
Вы можете присвоить List к Collection, но не List к Collection.
Представьте, что бы произошло, если бы это было возможно:
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 generics.
Поэтому было решено, что List - это Collection, но НЕ List (или Collection).
В качестве побочного комментария интересно отметить, что массивы не подчиняются тем же правилам: String[] is an Object[], и присваивания законны.