我只是举个例子,而不是用语言来尝试:
我有动物类,以及延伸动物的狗,鱼,猫等。
我有三种不同的方法,返回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():
}
}
或者至少,通过施放ala someMap = (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>>"
。
我做错了什么?
答案 0 :(得分:5)
public void <A extends Animal>
并不意味着“A
是任何扩展Animal
的类型”,这意味着“A
是扩展{{1}的特定类型之一}}”。您需要使用以下声明:
Animal
构造public void doSomething() {
Map<String, ? extends List<? extends Animal>> someMap;
// ...
}
是您表达“任何扩展? extends Animal
的类型”的方式。
您必须使用该声明的原因是,与直觉相反,泛型类型之间的子类型关系的工作方式与它们在常规类型之间的工作方式不完全一致。例如,Animal
是List<Dog>
的子类型。它是不 Collection<Dog>
或List<Animal>
等的子类型。不允许这样做的原因称为堆污染,也在Angelika Langer's FAQ。但是,Collection<Animal>
是List<Dog>
的子类型。 List<? extends Animal>
类型的变量可能已分配List<? extends Animal>
,List<Dog>
或List<Cat>
。重要的是,编译器不知道它是哪一个,只是它是其中之一。
就像List<Animal>
<{1}}的子类型一样,类似地认为List<Dog>
不是{的子类型{1}}。
证明为什么泛型以这种方式工作的最佳方式是矛盾的证明;也就是说,显示导致错误的(损坏的)代码示例是“直观地”工作的泛型。因此,如果List<Animal>
是Map<String, List<Dog>>
的子类型,则以下代码有效:
Map<String, List<? extends Animal>>
同样,对于您的List<Dog>
:
List<Animal>
答案 1 :(得分:0)
这里的问题在于泛型类型的工作方式。
在Java中,你会期望Number
是Integer
的超类(你会是对的)。您还希望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
类型及其子类型Dog
,Cat
和Fish
。将此逻辑扩展到您的动物,您就会明白为什么在将List<Dog>
投射到List<A>
时遇到问题。
现在,您可以使用addAll(Collection)
界面中定义的List
方法。
List<Animal> animals = new List<Animal>();
List<Dog> dogs = new List<Dog>();
animals.addAll(dogs);