【问题标题】:Adding objects to the "? extends" type of Generic Collections将对象添加到“?扩展”类型的通用集合
【发布时间】:2012-06-22 14:30:08
【问题描述】:
public void addAllAnimals(ArrayList<? extends Animal> animalLikeList){

 // I need to add animal objects (eg Dog, Cat that extends Animal) to animalLikeList.

}

我知道它不允许直接添加,因为? extends Animal 代表Animal 的未知子类型。

我的问题是:对于addaddAllAnimalAnimal 对象的子类型是否有任何方法(间接方式)animalLikeList

【问题讨论】:

  • 我编辑了它 Akhil。我想知道有没有办法(任何顽皮的方式)来添加元素。或者这真的是不可能的!
  • 如果你打算违反它的限制,你为什么要使用通配符,它​​没有意义?
  • 我将不同的动物列表(ArrayList, ArrayList) 传递给 addAllAnimals 方法。这就是我使用 addAllAnimals(ArrayList extends Animal> 的原因
  • 那么,如果将 Cat 添加到 Dogs List 中,如何确保不会造成堆污染?在这种情况下,这就是通配符限制的全部目的。它的存在是为了保护您的数据,而您想要违反它表明某些事情是不对的,恕我直言
  • 现在明白了 Edwin。+1 并同意!

标签: java generics collections


【解决方案1】:

不,没有直接的合法方式(除了不安全的类型转换)。您只能将元素添加到用super 声明的通用集合,而不是extends。使用 Josh Bloch 在Effective Java, 2nd Edition, Item 28 中推广的首字母缩略词 PECS(Producer - Extends, Consumer - Super)可能更容易记住。 p>

事实上,你所追求的似乎并没有直接意义。 List&lt;? extends Animal&gt; 可以是例如List&lt;Dog&gt;List&lt;Cat&gt;。前者只能接受Dog元素,后者只能接受Cats。而且由于您无法在 Java 中实例化通用(不可具体化)类型,因此您必须在运行时知道您正在处理一个狗列表,因此您只尝试将狗添加到其中(猫反之亦然)。这意味着您不能在本地和静态地创建要添加的对象 - 您也必须以通用方式获取它们,而且以允许编译器确保每次都匹配具体类型的方式。最简单的方法是将元素作为通用参数传递。所以这样做是可能且安全的:

public <E extends Animal> void addAllAnimals(List<E> animalLikeList, E animal) {
    animalLikeList.add(animal);
}

List<Dog> dogs = new ArrayList<Dog>();
List<Cat> cats = new ArrayList<Cat>();

addAllAnimals(dogs, new Dog());
addAllAnimals(cats, new Cat());

注意这个方法是泛型的,带有一个命名类型参数,可以保证列表的实际泛型类型与要添加到其中的元素类型相同。

您可以简单地将E 参数替换为Collection&lt;E&gt; 以一次添加多个对象。

【讨论】:

    【解决方案2】:

    我的问题是:是否有任何方式(间接方式)向animalLikeList 添加或添加Animal 或Animal 对象的子类型?

    在不更改签名的情况下,这是有充分理由的。假设您想将 Dog 值添加到该列表中......但假设它在逻辑上是一个猫列表:

    ArrayList<Cat> cats = new ArrayList<Cat>();
    addAllAnimals(cats);
    

    您不应该将Dog 添加到ArrayList&lt;Cat&gt;,对吗?

    如果您希望能够将任何种动物添加到列表中,您需要:

    public void addAllAnimals(ArrayList<Animal> animalLikeList) {
    

    public void addAllAnimals(ArrayList<? super Animal> animalLikeList) {
    

    【讨论】:

    • 谢谢乔恩。但我将不同的动物列表(ArrayList, ArrayList) 传递给 addAllAnimals 方法。这就是我使用 addAllAnimals(ArrayList extends Animal>. 的原因
    • @NamalFernando:那么您希望如何将Dog 添加到addAllAnimals 内的列表中?如果您尝试将Dog 添加到ArrayList&lt;Cat&gt;,您想要发生什么?你明白你是如何解决根本问题的吗?
    • @NamalFernando Jon 在这件事上已经说了很多。没有安全的方法可以添加到具有这种类型的列表中。
    • 是的 Jon 和 Jarkko。我完全同意你的看法。我想知道的是“有什么方法可以将 Animal 或 Animal 对象的 SubType 添加到 animalLikeList ?以上述方式或其他方式。+1 表示您的观点和很好的解释。
    【解决方案3】:

    您为什么不简单地更改方法签名以使用不变类型参数?

    public void addAllAnimals(ArrayList<Animal> animalLikeList){
       animalLinkedList.add(new Dog());
       animalLinkedList.add(new Cat());
    
    }
    

    它应该可以正常工作,并且通过使用不变的类型参数,您将被允许从列表中读取或写入,并且根据子类型多态性的规则,它可以包含您想要的任何种类的动物。

    不要尝试传递狗列表或猫列表,而是将动物列表传递给方法。

    【讨论】:

      【解决方案4】:
      ArraList<anything extends animal> list= new ArrayList<anything extends animal>();
      
      
      addAllAnimals(list);
      

      这是完全有效的。您的方法 addAllAnimals 将接受任何声明包含 Animal 的任何子类型的 arrayList。

      对于添加对象/列表,您可以添加任何对象/列表动物的子类型。

      【讨论】:

      • 我认为这甚至不会编译
      • 好吧,代替“anything extends animal”可能会有狗、猫等。我看不出它不能编译的任何原因
      【解决方案5】:

      不是以类型安全的方式...

      一个想法是将签名更改为:

      public static <T extends Animal> void addAllAnimals(ArrayList<? super T> list){}
      

      您可以将 T 或 T 的子类型添加到 T 列表中...

      【讨论】:

        【解决方案6】:

        您可以使用非泛型方式在泛型集合中添加所有内容

        public void testExtends(ArrayList<? extends Parent> list) throws Exception {
            /*  list.add(new Child());
                list.add(new Parent());
                list.add(new GrandParent());
                // can not add
                */
                /**
                 * Unsafe collection way
                 */
                ArrayList list2=new ArrayList();
                list.addAll(list2);
            }
        

        【讨论】:

          猜你喜欢
          • 2020-06-07
          • 1970-01-01
          • 2016-06-28
          • 1970-01-01
          • 2014-12-09
          • 2013-08-25
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多