Java的。收藏中的通配符

时间:2013-11-23 17:53:22

标签: java collections wildcard

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);        
    }
}

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

我在动物园为三个猫屋制作了ArrayLists。第一个是简单的ArrayList&lt;哺乳动物&gt;。我可以添加任何类型的哺乳动物或其子类。

第二个是ArrayList&lt; ?超级哺乳动物&gt;。我还可以添加任何类型的哺乳动物或其子类。

第三个是ArrayList&lt; ?延伸哺乳动物&gt;。我不能添加任何东西。

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

以下是我的问题:


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

2)我理解“&lt;?extends superclass&gt;”接受超类及其所有子类。 Ergo,&lt; ?延伸哺乳动物&gt;“接受哺乳动物,狮子和老虎。听起来就像”&lt;哺乳动物&gt;“。有什么区别?

3)我读到“&lt;?super className&gt;”接受任何className的超类。这听起来不对。在上面的例子中,狮子会不是哺乳动物的超类,但是“&lt;?super Mammal&gt;”接受狮子会。动物是哺乳动物的超类,但是“&lt;?super Mammal&gt;”不接受它。我想那里有错误的信息。

4)如果“&lt;?extends superclass&gt;”是只读的,我开始填充它的方式是什么?有一个只能读取的空列表有什么意义?

5)为什么addAll方法不适用于“&lt;?super className&gt;”?

我知道这些是基本问题,我知道他们之前已经得到了回答。我试图给出一个尽可能简单的代码示例,希望能给我一个尽可能清晰的答案。如果您有任何建议,请提前致谢。

2 个答案:

答案 0 :(得分:3)

我不会详细介绍。

List<Animal>表示:此列表包含Animal的实例。所以你可以添加你想要的任何动物,当你从这个列表中获取元素时你肯定会得到一个动物。

List<? extends Animal>表示:此列表是通用列表,但我们不知道泛型类型的类型。我们所知道的是泛型类型是Animal,或者Animal的任何子类。例如,它可以是List<Animal>List<Reptile>List<Lion>。但我们不知道。因此,从此列表中获取元素时,您可以保证获得一个Animal。但是你不会被允许在列表中添加任何内容,因为你不知道它的类型。如果您被允许添加某些内容,则可以将Lion存储在List<Reptile>中,该列表将不再是类型安全的。

List<? super Animal>表示:此列表是通用列表,但我们不知道泛型类型的类型。我们所知道的是,泛型类型是Animal,或者Animal的任何超类或超级接口。例如,它可以是List<Animal>List<Object>。所以,当从这个列表中获取一个元素时,你可以肯定的是它是一个Object。你可以做的肯定是在这个列表中添加你想要的任何动物,因为List<Animal>List<Object>都接受Animal,Lion,Reptile或Animal的任何子类。

大多数情况下,您将这些通配符用于方法的参数。

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

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

此规则称为PECS: Procucer:Extends;消费者:超级

如果您的方法将列表作为参数,并且必须从列表中获取元素并从中添加元素,那么您将使用List<Animal>

答案 1 :(得分:1)

如果您只想要一个包含Animal或子类Animal的列表,则应使用List<Animal>。我认为这是你大部分时间都需要的。

<强>延伸:

ArrayList<? extends Mammal> list = ..

这意味着list类型为Mammal 其子类之一。所以你可以这样做:

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

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

您也无法将Tiger添加到list。那是因为<? extends Mammal>没有告诉你有关Tiger的任何信息。 list类型Lion也是可能的。

<强>超:

对于<? super Mammal>,情况正好相反。 <? super Mammal>表示该列表的类型为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>