6

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;
}
Stim
  • 1,455
  • 14
  • 28

1 Answers1

6

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.

Community
  • 1
  • 1
Karthik T
  • 31,456
  • 5
  • 68
  • 87
  • However, List> expandedLists = (List>)list; is assignable. But I see that is regardless of the binding of T. – Stim Dec 20 '12 at 10:13
  • @Stim, im rusty in Java, but in C++ that would be an unsafe cast, infact I understand that [this would result in an error](http://stackoverflow.com/questions/262367/type-safety-unchecked-cast) had you not suspended it on purpose. If you were to check the type with `isinstanceof` I expect it will return false? – Karthik T Dec 20 '12 at 10:16
  • It is not possible to verify the parametrized type using instanceof in java. But you are right, the assignment causes compiler warnings. – Stim Dec 20 '12 at 10:22
  • Thanks @Karthik for pointing out the issue. With your help I was able to construct a legal assignment without a cast, see the question again. – Stim Dec 20 '12 at 10:35