【问题标题】:Java. Wildcards in Collections爪哇。集合中的通配符
【发布时间】:2013-11-23 17:53:21
【问题描述】:

Java。集合中的通配符

我在理解集合中的通配符时遇到了很多麻烦,即使在阅读了 Stack Overflow 和各种教程网站上的类似帖子之后也是如此。我在下面做了一个非常简单的例子。你能解释一下我将如何在Collection< myClass >Collection< ? Extends MyClass >Collection< ? Super MyClass > 之间进行选择吗?

package z5;
import java.util.ArrayList;
public class Z5 {
public static class Animal{
}
public static class Mammal extends Animal{   
}
public static class Reptile extends Animal{   
}
public static class Lion extends Mammal{   
}
public static class Tiger extends Mammal{   
}
public static class Snake extends Reptile{
}           
    public static void main(String[] args) {
        ArrayList<Mammal> catHouse1 = new ArrayList<Mammal>();
            catHouse1.add(new Lion());
            catHouse1.add(new Tiger());  
            catHouse1.add(new Mammal());
            catHouse1.add(new Animal()); //ERROR  
        ArrayList<? super Mammal> catHouse2 = new ArrayList<Mammal>();
            catHouse2.add(new Lion());
            catHouse2.add(new Tiger());
            catHouse2.add(new Mammal());
            catHouse2.add(new Animal()); //ERROR  
        ArrayList<? extends Mammal> catHouse3 = new ArrayList<Mammal>();
            catHouse3.add(new Lion());  //ERROR
            catHouse3.add(new Tiger());  //ERROR
            catHouse3.add(new Mammal());  //ERROR
            catHouse3.add(new Animal()); //ERROR 
        ArrayList<Mammal> zooMammals = new ArrayList<Mammal>();
            zooMammals.addAll(catHouse1);  
            zooMammals.addAll(catHouse2);  //ERROR
            zooMammals.addAll(catHouse3);
        ArrayList<Animal> zooAnimals = new ArrayList<Animal>();
            zooAnimals.addAll(catHouse1);
            zooAnimals.addAll(catHouse2);  //ERROR
            zooAnimals.addAll(catHouse3);        
    }
}

在上面的示例中,我创建了类的层次结构。动物是哺乳动物和爬行动物的超类,哺乳动物是狮子和老虎的超类。

我为动物园里的三个猫屋制作了 ArrayList。第一个是简单的 ArrayList。我可以添加任何类型的哺乳动物或其子类的对象。

第二个是 ArrayList。我还可以添加任何类型的哺乳动物或其子类的对象。

第三个是 ArrayList。我不能添加任何东西。

最后,我将我的三个猫屋添加到动物园收藏中。 Animal 和 Mammal 是这里的主要超类,无论接收者 ArrayList 持有哪种类型,行为都是相同的。 ArrayList 和 ArrayList 可以添加到动物园。 ArrayList 不能。

这是我的问题:


1) 如果我想创建一个包含某个超类的所有子类的数组,为什么需要通配符?我不能只声明所有 ArrayList 并获得我需要的功能吗?

2) 我了解“”接受超类及其所有子类。因此," 接受 Mammals、Lions 和 Tigers。这听起来完全像 ""。有什么区别?

3) 我读到“”接受任何作为 className 超类的类。这听起来不对。在上面的例子中,Lions 不是 Mammals 的超类,但是 " super Mammal >" 接受 Lions。动物是哺乳动物的超类,但“”不接受它。我认为我的信息有误。

4) 如果 "" 是只读的,我如何开始填充它?拥有一个只能读取的空列表有什么意义?

5) 为什么addAll方法对“”不起作用?

我知道这些是基本问题,而且我知道之前已经回答过这些问题。我试图给出一个尽可能简单的代码示例,并希望能给我一个尽可能清晰的答案。提前感谢您提供的任何建议。

【问题讨论】:

标签: java collections wildcard


【解决方案1】:

我不会详细介绍。

List&lt;Animal&gt; 表示:此列表包含 Animal 的实例。所以你可以添加任何你想要的Animal,并且在从这个列表中获取元素时,你一定会得到一个Animal。

List&lt;? extends Animal&gt; 表示:这个列表是泛型列表,但是我们不知道泛型类型的类型。我们所知道的只是泛型类型是 Animal 或 Animal 的任何子类。例如,它可能是List&lt;Animal&gt;List&lt;Reptile&gt;List&lt;Lion&gt;。但我们不知道。所以,当从这个列表中获取一个元素时,你肯定会得到一个 Animal。但是你不会被允许在列表中添加任何东西,因为你不知道它的类型。如果允许您添加某些内容,您可以将 Lion 存储在 List&lt;Reptile&gt; 中,并且该列表将不再是类型安全的。

List&lt;? super Animal&gt; 表示:这个列表是泛型列表,但是我们不知道泛型类型的类型。我们所知道的只是泛型类型是Animal,或者Animal的任何超类或超接口。例如,它可能是List&lt;Animal&gt;List&lt;Object&gt;。所以,当从这个列表中获取一个元素时,你可以肯定地说它是一个对象。不过,您可以确定的是在此列表中添加您想要的任何动物,因为List&lt;Animal&gt;List&lt;Object&gt; 都接受动物、狮子、爬行动物或动物的任何子类。

大多数时候,您使用这些通配符作为方法的参数。

当方法将列表作为参数并且只对从列表中读取感兴趣(即列表是生产者)时,您将使用List&lt;? extends Animal&gt;。这使您的方法更可重用,因为它可以与List&lt;Animal&gt; 一起使用,也可以与List&lt;Reptile&gt;List&lt;Lion&gt; 等一起使用。

当方法将列表作为参数并且只对向列表添加元素感兴趣(即列表是消费者)时,您将使用List&lt;? super Animal&gt;。这使您的方法更可重用,因为它可以与List&lt;Animal&gt; 一起使用,也可以与List&lt;Object&gt; 一起使用。

这条规则被称为 PECS:Procucer: Extends;消费者:超级

如果您的方法将列表作为参数并且必须从列表中获取和添加元素,您将使用List&lt;Animal&gt;

【讨论】:

  • “大多数时候,您使用这些通配符作为方法的参数。”谢谢你,谢谢你,谢谢你。我试图通过创建特定类型的集合来理解这一点,但我找不到这样做的充分理由(仍然不能)。但我没想到它是一种方法的参数类型。通配符使方法更有用,因为它允许许多不同的集合类型使用它们。
  • 所以,在我的 main 方法中保存了爬行动物、哺乳动物和两栖动物的列表以跟踪动物园中的一切,我应该将这些列表类型声明为它们的特定超类:List、List、列出等
  • 如果我有想对所有动物使用的方法,我应该用通配符声明它们的参数: public double totalWeight (List extends Animal> l){//a foreach 循环添加每个动物物体的重量并返回平均值}
【解决方案2】:

如果您只想要一个可以包含Animal 或子类Animal 的列表,您应该使用List&lt;Animal&gt;。我认为这是您最需要的。

扩展:

ArrayList<? extends Mammal> list = ..

这意味着listMammal 类型它的子类之一。所以你可以这样做:

ArrayList<? extends Mammal> list = new ArrayList<Tiger>(); // List of type Tiger

使用&lt;? extends Mammal&gt; 时,您不能在list 中插入任何内容(null 除外)。没有类型安全的方法来插入任何东西的原因。您不能将Mammal 添加到list,因为list 可能是Tiger 类型(如上例所示)。 Mammal 不是 Tiger 所以这是行不通的。

您也不能将Tiger 添加到list。那是因为&lt;? extends Mammal&gt; 没有告诉你任何关于Tiger 的信息。 list 也有可能是 Lion 类型。

超级:

&lt;? super Mammal&gt; 是相反的情况。 &lt;? super Mammal&gt; 表示列表的类型为Mammal 它的超类之一。你可以这样做:

ArrayList<? super Mammal> list = new ArrayList<Animal>(); // List of type Animal

您可以将Mammal 的任何子类添加到此列表中。这是安全的,因为Mammal 的子类始终是Animal 的子类。但是,除了Object 之外,您无法从列表中获取任何内容。那是因为你不知道确切的列表类型。

Mammal m = list.get(0); // can't work because list can be a List<Animal>

【讨论】:

    猜你喜欢
    • 2023-02-17
    • 1970-01-01
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-25
    • 2017-10-01
    • 2023-03-04
    相关资源
    最近更新 更多