在java中将子类添加到通配符类型的泛型集合中

时间:2014-06-10 16:42:15

标签: java generics

这是合法的:

List<? extends Animal> animals = getMonkeys();
...
List<Monkey> getMonkeys(){
    ...
}

以下导致编译器错误:

animals.add(new Donkey());

为什么呢?如何合法添加子类型?

2 个答案:

答案 0 :(得分:4)

编译器不知道通配符可以是哪个子类。对于所有人都知道,它可能是List<Fish>。为了保护类型安全,编译器必须阻止对add的调用,因为不应该允许对Monkey添加List<Fish>

List<? extends Animal> animals = new ArrayList<Fish>();  // legal
animals.add(new Monkey());  // unsafe; compiler error
animals.add(new Donkey());  // also unsafe; compiler error

为了防止这种情况,您需要消除变量中的通配符,以便编译器知道泛型类型参数。 List<Animal>List<Monkey>(或List<Donkey>视情况而定)允许您使用add以外的参数调用null。如果您必须致电getMonkeys(),则必须使用其返回类型List<Monkey>

List<Monkey> monkeys = getMonkeys();
monkeys.add(new Monkey());

答案 1 :(得分:2)

只需将列表声明为List<Animal>即可。从Animal扩展的类的任何对象都可以插入那里。

当您遍历集合的元素并希望/需要集合中的元素属于特定类时,在List<? extends Foo>中使用通配符。例如:

class Animal {
    public String getSpecie() {
        return "generic animal";
    }
}

class Donkey extends Animal {
    @Override
    public String getSpecie() {
        return "generic donkey";
    }
}

class Mokney extends Animal {
    @Override
    public String getSpecie() {
        return "generic monkey";
    }
}

//some method in an utility class...
//we are declaring that only List<Animal> or List<some class that extends animal> can be passed as argument
public void printSpecies(List<? extends Animal> animalList) {
    //we can be sure every element in animalList is an animal always
    for (Animal animal : animalList) {
        System.out.println(animal.getSpecie());
    }
}

//calling the method above...
List<Monkey> monkeyList = ...
printSpecies(monkeyList); //compiles and works
List<Donkey> donkeyList = ...
printSpecies(donkeyList); //compiles and works
List<String> stringList = ...
printSpecies(stringList); //doesn't compile