使用Generic Class和Generic Collections类型的声明之间的区别是什么

时间:2014-01-23 17:39:30

标签: java generics java-5

我将向您展示两种仿制品声明。在part1中,我在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();
        }
    }
}

2 个答案:

答案 0 :(得分:6)

在第2部分中,genericanimal.genList的类型为List<T> = List<Animal>。在第1部分中,列表的类型为List<? extends Animal>

问题在于List<? extends Animal>表示“动物某些特定亚型的列表未知”。例如,您可以编写List<? extends Animal> list = new ArrayList<Cat>()。并且你不应该能够将任何动物添加到猫的列表中。通过写List<? extends Animal>,你说你想故意忘记哪种类型的动物被允许进入列表,尽管你知道列表中的任何内容都是一些动物类型。

答案 1 :(得分:0)

  

为什么在part1中,它在尝试将Animal对象添加到列表时引发了错误?

List<? extends Animal> totList = new ArrayList<Animal>();
totList.add(new Animal("Animal1",21));  // Error: Unresolved compilation problem:

因为编译器限制在使用上限有效通配符声明的列表上添加对象(?extends Animal)。编译器不确定列表是否输入动物,猫或狗。

很简单,只要使用Upper bounded Wildcard声明变量,就不允许在该变量中插入元素。

使用上限通配符的

优点是:

  1. 您可以创建一个只能读取所有元素的方法(但不能 插入列表。
  2. 您可以将相同的方法重复用于Animal列表或Dog列表 或者Cat。列表。
  3. 您也可以安全地在元素上应用Animal的所有方法。
  4.   

    为什么在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. 您可以将元素插入List,但元素必须是类型 吨。
    3. 您可以重复使用Animal对象或Cat对象或Dog的方法 对象。
    4. 您可以安全地在元素上应用所有Animal方法。