I stumbled upon this interesting cast/generics problem today:
public class A {
Map<Class<? extends B>, List<Set<B>>> mapListSet = new HashMap<>();
Map<Class<? extends B>, Set<B>> mapSet = new HashMap<>();
public <T extends B> List<Set<T>> foo(Class<T> clazz) {
List<Set<T>> listSet = (List<Set<T>>) mapListSet.get(clazz);
return listSet;
}
public <T extends B> Set<T> bar(Class<T> clazz) {
Set<T> set = (Set<T>) mapSet.get(clazz);
return set;
}
}
class B {
}
I can compile the method "bar" with only one warning from the IDE, whereas the IDE completely refuses to compile method "foo". This is a simplified example taken from my recently written code, does anyone know if I can make it more elegant rather than just doing this?
public <T extends B> List<Set<T>> foo(Class<T> clazz) {
List<Set<T>> listSet = (List) mapListSet.get(clazz);
return listSet;
}
Any help is hugely appreciated, I have a feeling this code smells really bad and I would love to improve on it.
My other answer shows how the cast you ask for can be done.
But! On a second though I think there is a better solution: Change the field types to this:
class A {
Map<Class<? extends B>, List<? extends Set<? extends B>>> mapListSet = new HashMap<>();
Map<Class<? extends B>, Set<? extends B>> mapSet = new HashMap<>();
....
In that way you don't need the cast-through-extra-type trick, it works without it.
And, more importantly: The field types express the actual type of the elements.
Your current types express that the elements are Set<B>
. But they're not! They are not sets of type B
, they are sets of some unknown subtype to B
. And that is exactly what Set<? extends B>
expresses.