如何将继承对象列表转换为Java中的对象集合?

时间:2010-05-02 20:00:16

标签: java generics collections

我有一个集合类型:

Collection<A> collecA

我的对象中有一个列表:

List<B> listB

B在哪里延伸A

class B extends A { ... }

但我无法做到以下几点:

collecA = listB

我无法理解为什么Collection是由List实现的。

4 个答案:

答案 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 实例的引用

或者更一般地说: BA的子类暗示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 [],并且赋值是合法的。