使用泛型类和泛型集合类型声明的区别是什么?

4

我将展示两种泛型声明的样式。在第一部分中,我使用了List上的泛型上界声明,如下所示:

List<? extends Animal> totList = new ArrayList<Animal>();

但是如果你尝试将一个Animal对象添加到列表中,就会出现以下错误:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    The method add(capture#1-of ? extends Animal) in the type List<capture#1-of ? extends Animal> is not applicable for the arguments (Animal)

    at GenericsType.main(GenericsType.java:39)

但是就像在Part2中一样,如果我按照以下格式在泛型类内声明列表,在向列表中添加(Animal对象)或(Animal子类对象)时不会抛出任何错误。
class GenericAnimal<T extends Animal> 
{    
   List<T> genList = new ArrayList<T>();
}

在part2中为什么没有抛出错误,两种声明风格有什么区别。
示例代码:
1.Animal.java
public class Animal {
    private String name;
    private int height;

    public void animalJump()
    {
        if(height>100)
        {
            System.out.println(name+" with height-"+height+" can JUMP");
        }
        else
            System.out.println(name+" with height-"+height+" cannot jump");         
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public Animal(String name, int height) {
        setName(name);
        setHeight(height);
    }

}

2.GenericsType.java

import java.util.*;
import Animal;
import Animal.Cannine;
import Animal.Feline;
import Animal.Feline.Cat;
import Animal.Cannine.Dog;

public class GenericsType {

    public static List<? extends Animal> totList = new ArrayList<Animal>();

    public static void processAllfunc1()
    {
        for(Animal a : totList)
        {
            a.animalJump();
        }

    } 

    public static void main(String args[])
    {    
       // Part 1
        totList.add(new Animal("Animal1",21));  // Error: Unresolved compilation problem:           
        processAllfunc1();


       // Part 2
        GenericAnimal<Animal> genericanimal = new GenericAnimal<Animal>();
        genericanimal.genList.add(new Animal("Animal2",22));  // No Error, why?
        genericanimal.genList.add(new Cat("Cat4",204));      // No Error for Cat also, why?

        genericanimal.processAllfunc2();
    }
}

3.GenericAnimal.java

public class GenericAnimal<T extends Animal> {  

    public List<T> genList = new ArrayList<T>();

    public void processAllfunc2()  {
        for (T a : genList) {
            a.animalJump();
        }
    }
}

可能是 Java 泛型通配符 的重复问题。 - Brian Roach
1
复制,但措辞更好。另一个应该是这个的副本。 - Sled
2个回答

6
在第二部分中,genericanimal.genList的类型是List<T> = List<Animal>。而在第一部分中,列表的类型是List<? extends Animal>
问题在于List<? extends Animal>表示“一个未知的Animal特定子类型的列表”。例如,您可以编写List<? extends Animal> list = new ArrayList<Cat>()。而且,您不应该能将任何动物添加到猫的列表中。通过编写List<? extends Animal>,您正在表明您希望有意失去允许进入列表中的动物类型的跟踪,尽管您知道列表中的内容是某种类型的动物。

0
为什么在part1中,试图将一个Animal对象添加到列表中时会抛出错误?
List<? extends Animal> totList = new ArrayList<Animal>();
totList.add(new Animal("Animal1",21));  // Error: Unresolved compilation problem:

因为编译器限制了在使用上界通配符(? extends Animal)声明的列表中添加对象。编译器无法确定列表是否被类型化为Animal、Cat或Dog。

简单来说,每当使用上界通配符声明变量时,就不允许向该变量插入元素。

使用上界通配符的优点是:

  1. 您可以创建一个仅读取列表中所有元素(但不插入)的方法。
  2. 您可以将相同的方法重用于Animal列表、Dog列表或Cat列表。
  3. 此外,您可以安全地将Animal的所有方法应用于元素。

为什么在part2中,添加Animal对象和Cat对象到列表中时没有抛出错误?

public List<T> genList = new ArrayList<T>();

genericanimal.genList.add(new Animal("Animal2",22));  // No Error, why?
genericanimal.genList.add(new Cat("Cat4",204));

因为这里的genList实际上是Animal类型,就像下面的格式一样

List<Animal> genList = new ArrayList<Animal>();

现在就像普通的特定类型列表一样,它可以添加该类型的元素。

因此,Animal对象可以添加到List中。同时,Cat对象也允许添加,因为它只是Animal对象的子类(Cat只是Animal的一个子类)

使用<T extends Animal>的优点是:

  1. 您可以读取列表中的所有元素。
  2. 您可以向列表中插入元素,但元素必须是T类型。
  3. 您可以重用Animal对象、Cat对象或Dog对象的方法。
  4. 您可以安全地将所有Animal方法应用于元素。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接