我有一个集合类型:
Collection<A> collecA
我的对象中有一个列表:
List<B> listB
B在哪里延伸A
class B extends A { ... }
但我无法做到以下几点:
collecA = listB
我无法理解为什么Collection是由List实现的。
答案 0 :(得分:16)
让我们假设您可以做您所描述的内容:
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
的列表中
我无法理解为什么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
的{{1}}。但是,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 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<Lion>
是Cage<Animal>
的子类型吗?”。通过动物笼的上述定义,答案必须是“否”。这太令人惊讶了!但是当你想到它时,它就变得非常有意义:狮子笼不能被认为是留在蝴蝶中,并且不能假定蝴蝶笼藏在狮子里。因此,两个笼子都不能被视为“全动物”笼子:
animalCage = lionCage; // compile-time error
animalCage = butterflyCage; // compile-time error
“
答案 1 :(得分:8)
Collection<? extends A> collecA
这解决了它。问题不是List extends Collection
,而是泛型类型。
答案 2 :(得分:5)
Java泛型不是协变。
有关详细信息,请参阅Java Theory and Practice: Generics Gotchas。
该页面显示了一个简单的例子,如果它是协变的话会破坏类型系统:
想象一下,你可以分配一个List&lt; Integer&gt;到列表&lt; Number&gt;。然后,以下代码将允许您将不是Integer的内容放入List&lt; Integer&gt;中:
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!
答案 3 :(得分:1)
您可以指定列表&lt; B&gt;收集&lt; B&gt;,但不是List&lt; B&gt;收集&lt; A&gt;。
想象一下如果可能会发生什么:
List<B> = new ArrayList<B>();
Collection<A> collecA = listB; //Assume that this line compiles
collecA.add(new A());
B item = listB.get(0); //ClassCastException!
如您所见,我们通过向一个应该只包含B类(或后代)对象的集合中添加具体类型A的实例来“欺骗”泛型类型系统。 因此,对B执行隐式强制转换的最后一行失败并出现ClassCastException。它出什么问题了?编译器不能保证类型安全,这违反了Java泛型原则之一。
因此,已经确定List&lt; B&gt;是Collection&lt; B&gt;,但NOT List&lt; A&gt; (或收集&lt; A&gt;)。
作为附带注释,有趣的是注意数组不遵循相同的规则:String [] 是一个Object [],并且赋值是合法的。