Rather than attempt this in words, I’ll just give an example:
I have an Animal class, as well as a Dog,Fish,Cat, etc. which extend Animal.
I have three different methods, which return Map<String,List<Dog>>, Map<String,List<Fish>>, Map<String,List<Cat>>. We’ll call these getDogMap, getCatMap, and getFishMap.
I am writing a generic method which, depending on various parameters, calls one of these methods. Here is what I expected to be allowed to do:
public void <A extends Animal> doSomething(){
Map<String,List<A>> someMap;
if(someCondition){
someMap = getDogMap();
}else if(anotherCondition){
someMap = getFishMap();
}else{
someMap = getCatMap():
}
}
Or at least, that with casting ala someMap = (Map<String,List<Dog>>) getDogMap();
However, this does not work. Eclipse tells me "Type mismatch: cannot convert from Map<String,List<Dog>> to Map<String,List<A>>" If I try to force the cast, it tells me "Cannot cast from Map<STring,List<Dog>> to Map<String,List<A>>".
What am I doing wrong?
public void <A extends Animal>doesn’t mean “Ais any type that extendsAnimal“, it means “Ais a specific one of the types that extendsAnimal“. You need to use the following declarations:The construct
? extends Animalis how you express “any type that extendsAnimal“.The reason you have to use that declaration is that, counter-intuitively, the way subtype relationships between generic types work isn’t exactly consistent with how they work between regular types. For instance,
List<Dog>is a subtype ofCollection<Dog>. It is not a subtype ofList<Animal>, orCollection<Animal>etc. The reason why this isn’t allowed is called heap pollution, also explained in Angelika Langer’s FAQ.List<Dog>is, however, a subtype ofList<? extends Animal>. A variable of typeList<? extends Animal>may have assigned aList<Dog>, or aList<Cat>, or aList<Animal>. The important part is that the compiler doesn’t know which of these it is, just that it is one of them.Just like
List<Dog>is not a subtype ofList<Animal>, analogously it holds thatMap<String, List<Dog>>is not a subtype ofMap<String, List<? extends Animal>>.The best way to demonstrate why generics work this way is proof by contradiction; that is, showing examples of (broken) code that would lead to errors were generics to work “intuitively”. So, if
List<Dog>were a subtype ofList<Animal>, the following code would be valid:Similarly, for your
Map: