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.
To do what you want to do you have to circumvent the type system a little.
You have to trick the compiler by going through an extra type:
List<Set<T>> listSet = (List<Set<T>>) (Object) mapListSet.get(clazz); // Compiles
The problem is, as racraman notes, that T extends B does not imply that List<Set<T>> extends List<Set<B>>, and since the compiler doesn't see how the cast could ever work it generates an error. It is the same reason as you can't cast, for example String to Number. Since they have no subtype relationship the compiler thinks the cast just can't be right.
It looks like you keep track of the member types of each set at runtime, using a Class as key. It that situation it might be justified to use the above trick.
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