javagenerics

Problem with casting a nested generic Set


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.


Solution

  • 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.