【问题标题】:How to cast a list of inheriting objects to a collection of objects in Java?java - 如何将继承对象列表转换为Java中的对象集合?
【发布时间】:2026-02-19 09:55:02
【问题描述】:

我有一个集合类型:

Collection<A> collecA

我的对象中有一个列表:

List<B> listB

B 延伸 A

class B extends A { ... }

但我不能执行以下操作:

collecA = listB

我不明白为什么,因为 Collection 是由 List 实现的。

【问题讨论】:

    标签: java generics collections


    【解决方案1】:

    让我们暂时假设您可以按照您的描述进行操作:

    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 是一个集合 持有As。但是,如果允许上述分配,那么我们有一个 问题因为collecA 确实引用了List&lt;B&gt; 实例 - 我只是 将A 添加到只能包含Bs 的列表中!

    提问者还说:

    我不明白为什么,因为 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&lt;A&gt;变量只能引用可以容纳As的Lists。但是,List&lt;B&gt; instance 不能容纳 As。因此,像listA 这样的List&lt;A&gt; 变量 不能被分配对listB 引用的List&lt;B&gt; 实例 的引用。

    或更一般地说:BA 的子类暗示 SomeGenericClass&lt;B&gt;SomeGenericClass&lt;A&gt; 的子类 (JLS §4.10子类型不通过泛型扩展:T &lt;: U 并不意味着C&lt;T&gt; &lt;: C&lt;U&gt;


    Java 泛型教程中的这个示例/类比帮助我理解了这一点:

    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 = ...;
    

    但是“动物笼子”呢?英语是模棱两可的,所以准确地说让我们假设我们正在谈论一个“all-animal cabinet”

    Cage<Animal> animalCage = ...;
    

    这是一个笼子,旨在容纳各种动物,混合在一起。它必须有足够坚固的栏杆以容纳狮子,并且间隔足够紧密以容纳蝴蝶。
    ...
    既然狮子是一种动物(狮子是动物的子类型),那么问题就变成了,“狮子笼子是动物笼子的一种吗?Cage&lt;Lion&gt;Cage&lt;Animal&gt; 的子类型吗?”。根据上述动物笼子的定义,答案一定是“否”。这很令人惊讶!但仔细想一想,这是完全有道理的:不能假设狮子笼可以关在蝴蝶里,也不能假设蝴蝶笼可以关狮子。因此,这两个笼子都不能被认为是“全动物”笼子:

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

    "

    【讨论】:

    • 好容易理解的解释伯特。在尝试理解泛型和继承时,我一直很欣赏愚蠢的类比。 Kartoch 应该检查一下。
    • @Tamon - 很高兴您(希望其他人)发现它易于理解。我在理解泛型和继承时遇到了同样的问题(更不用说泛型通配符了),所以我使用了一个新的例子,比如 Java 泛型教程中的一个例子来让我根深蒂固。我搜索了“java 通用铸造动物笼”,以准确记住我在哪里读到这个类比。 :-)
    【解决方案2】:
    Collection<? extends A> collecA
    

    这解决了它。问题不是List extends Collection,而是泛型类型。

    【讨论】:

      【解决方案3】:

      Java 泛型不是协变的

      更多详情请参阅Java Theory and Practice: Generics Gotchas

      该页面显示了一个简单的示例,如果它是协变的,则会破坏类型系统:

      想象一下,您可以将 List 分配给 List。然后下面的代码将允许您将不是 Integer 的内容放入 List

      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!
      

      【讨论】:

        【解决方案4】:

        您可以将 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!
        

        如您所见,我们通过将具体类型 A 的实例添加到本应只有 B 类型(或后代)对象的集合中来“欺骗”泛型类型系统。 因此,对 B 执行隐式强制转换的最后一行失败并出现 ClassCastException。它出什么问题了?编译器无法保证类型安全,这违反了 Java 泛型原则之一。

        因此,决定 List 是 Collection,而不是 List(或 Collection)。

        作为旁注,有趣的是,数组不遵循相同的规则:String[] 一个 Object[],并且赋值是合法的。

        【讨论】: