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 T
is a subtype of List<K>
, it does NOT imply that List<T>
is a subtype of List<List<K>>
or that Map<Integer,List<T>>
is a subtype of Map<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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With