1

与其用文字来尝试,我只举一个例子:

我有一个 Animal 类,以及一个扩展 Animal 的 Dog、Fish、Cat 等。

我有三种不同的方法,它们返回Map<String,List<Dog>>, Map<String,List<Fish>>, Map<String,List<Cat>>. 我们将它们称为 getDogMap、getCatMap 和 getFishMap。

我正在编写一个通用方法,它根据各种参数调用这些方法之一。这是我期望被允许做的事情:

public void <A extends Animal> doSomething(){

    Map<String,List<A>> someMap;

    if(someCondition){
        someMap = getDogMap();
    }else if(anotherCondition){
        someMap = getFishMap();
    }else{
        someMap = getCatMap():
    }
}

或者至少,与演员 alasomeMap = (Map<String,List<Dog>>) getDogMap();

但是,这不起作用。Eclipse 告诉我"Type mismatch: cannot convert from Map<String,List<Dog>> to Map<String,List<A>>"如果我尝试强制转换,它会告诉我"Cannot cast from Map<STring,List<Dog>> to Map<String,List<A>>"

我究竟做错了什么?

4

2 回答 2

5

public void <A extends Animal>并不意味着“A是任何扩展的类型Animal”,而是意味着“A是扩展的特定类型之一Animal”。您需要使用以下声明:

public void doSomething() {
    Map<String, ? extends List<? extends Animal>>  someMap;
    // ...
}

构造? extends Animal是您表达“任何扩展的类型Animal”的方式。

您必须使用该声明的原因是,与直觉相反,泛型类型之间的子类型关系的工作方式与常规类型之间的工作方式并不完全一致。例如,List<Dog>是 的子类型Collection<Dog>。它不是List<Animal>, or etc 的子类型。Collection<Animal>不允许这样做的原因称为堆污染,在Angelika Langer 的常见问题解答中也有解释。List<Dog>然而,它是 的子类型List<? extends Animal>。一个类型的变量List<? extends Animal>可能已经分配了一个List<Dog>,或一个List<Cat>,或一个List<Animal>。重要的部分是编译器不知道它是其中的哪一个,只是知道它是其中之一。

就像List<Dog>is not的子类型一样List<Animal>,类似地它认为 thatMap<String, List<Dog>>不是子类型Map<String, List<? extends Animal>>

证明泛型为什么以这种方式工作的最好方法是反证法。也就是说,显示会导致错误的(损坏的)代码示例是“直观地”工作的泛型。因此,如果List<Dog>是 的子类型List<Animal>,则以下代码将是有效的:

List<Dog> dogs = new ArrayList<Dog>();
List<Animal> animals = dogs; // unsafe cast

// this operation violates type safety
animals.add(new Cat());

// would assign a Cat to a variable of type Dog without a compile error!
Dog dog = animals.get(0);

同样,对于您的Map

Map<String, List<Dog>> dogses = new HashMap<String, List<Dog>>();
Map<String, List<? extends Animal>> animalses = dogses; // unsafe cast

List<Cat> cats = new ArrayList();
cats.put(new Cat());
animalses.put("cats", cats);

List<Dog> dogs = dogses.get("cats");
Dog dog = dogs.get(0); // uh-oh
于 2012-08-18T01:06:37.617 回答
0

这里的问题在于泛型类型的工作方式。

在 Java 中,您会期望这NumberInteger(而且您是对的)的超类。你也会认为那Number[]Integer[]. 你也是对的。但是,您不能说它List<Number>List<Integer>. 这违反了类型安全。

List<Integer> intList = new ArrayList<Integer>();
List<Number> numList = intList; // the system stops you here
numList.add(Math.PI); // so that you don't do this

在您的情况下,您有一个类型Animal及其子类型DogCatFish。将此逻辑扩展到您的动物,您就会明白为什么无法转换List<Dog>List<A>.

现在,您可以使用接口addAll(Collection)中定义的方法。List

List<Animal> animals = new List<Animal>();
List<Dog> dogs = new List<Dog>();
animals.addAll(dogs);
于 2012-08-18T01:09:50.720 回答