我有一个集合类型:
Collection<A> collecA
我在对象中有一个列表:
List<B> listB
B 在扩展 A
class B extends A { ... }
但我无法执行以下操作:
collecA = listB
我无法理解为什么 Collection 由 List 实现。
我有一个集合类型:
Collection<A> collecA
我在对象中有一个列表:
List<B> listB
B 在扩展 A
class B extends A { ... }
但我无法执行以下操作:
collecA = listB
我无法理解为什么 Collection 由 List 实现。
假设您能够做到您所描述的事情:
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
的列表中! Asker 还说:
即使使用两个列表,这个赋值也是非法的,并不重要的是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>
变量只能引用可以容纳A
的List
。然而,List<B>
实例不能容纳A
。因此,像listA
这样的List<A>
变量不能被分配一个由listB
引用的List<B>
实例。
或者更一般地说: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
"
Collection<? extends A> collecA
这样修复了。问题不在于List extends Collection
,而是泛型类型。
Java泛型不是协变的。
请参见Java理论与实践:泛型陷阱以了解更多细节。
该页面展示了一个简单的例子,如果它是协变的,将会破坏类型系统:
想象一下,你可以将List<Integer>分配给List<Number>。那么以下代码将允许你将非Integer类型的内容放入List<Integer>中:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415)); // ERROR: Adds a float to list, 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!
正如您所看到的,我们通过向一个只应该包含类型为B(或其子类)对象的集合中添加一个具体类型A的实例来“欺骗”泛型类型系统。
因此,执行隐式转换为B的最后一行代码会导致ClassCastException。这是什么问题?编译器无法保证类型安全,这违反了Java泛型原则之一。
因此,已经决定List<B>是Collection<B>,但不是List<A>(或Collection<A>)。
顺便说一句,有趣的是数组不遵循相同的规则:String[]是Object[],并且赋值是合法的。