Home > Mobile >  Problem with casting a nested generic Set
Problem with casting a nested generic Set

Time:12-26

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.

CodePudding user response:

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.

CodePudding user response:

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 then have no subtype relationship the compiler things the cast jsut can't be right.

It looks like you keep track of them member types of each set at runtime, using a Class as key. It than situation it might be justified to use the above trick.

  • Related