Recently I was refactoring a generic method when I got into generic casting issues I cannot explain. Finally I realized I could do without the T type altogether (just inline it myself), but I’m still curious as to why the convert fail.
I created this minimal example to illustrate the issue.
Can someone explain me why the convert fails and the workaround works?
public <K, T extends List<K>> void castLists(List<T> list, K kForBinging) {
Map<Integer, List<T>> map = mapSizeToList(list);
// Type mismatch: cannot convert from Map<Integer,List<T>> to Map<Integer,List<List<K>>>
// Map<Integer, List<List<K>>> expandedMap = map;
// Added after accepting answer, legal assignment:
Map<Integer, ? extends List<? extends List<K>>> expandedMap = map;
// Originally proposed 'work around'
Map<Integer, ?> lessSpecific = map;
@SuppressWarnings("unchecked")
Map<Integer, List<List<K>>> canCast = (Map<Integer, List<List<K>>>)lessSpecific;
// ...
}
public <A> Map<Integer, List<A>> mapSizeToList(List<A> list) {
Map<Integer, List<A>> map = Maps.newHashMap();
// ...
return map;
}
I believe you need Covariance with generics before you can do such things. This doesnt seem to be supported by Java.
i.e in Java, if
Tis a subtype ofList<K>, it does NOT imply thatList<T>is a subtype ofList<List<K>>or thatMap<Integer,List<T>>is a subtype ofMap<Integer, List<List<K>>>. This is why the assignment errors out.Covariance would allow you to do this because with it, if template parameters have a subclass-superclass relationship, the defined classes will also have the exact same relationship. This would make this assignment possible. Scala (among other (functional programming?) languages) supports covariance and its complement contravariance.