Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn’t Map<String, Set<String>> match Map<T, Set<?>>? [duplicate]

I have a method with the following signature:

public <T> int numberOfValues(Map<T, Set<?>> map)

However I can’t call it passing in a Map<String, Set<String>>. For instance, the following doesn’t compile:

Map<String, Set<String>> map = new HashMap<>();
numberOfValues(map);

The error message being that:

numberOfValues (java.util.Map<java.lang.String,java.util.Set<?>>) in class cannot be applied to (java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)

However, if I change to the following all is fine:

public <T, V> int numberOfValues(Map<T, Set<V>> map)

However I’m not at all interested in V, as I just want to know the size of each of the sets.

For completeness sake, this is the whole method:

public <T, V> int numberOfValues(Map<T, Set<V>> map) {
    int n = 0;
    for (T key : map.keySet()) {
        n += map.get(key).size();
    }
    return n;
}

Which I’m aware it can also be accomplished like this, but isn’t the point of the question :)

public <T> int numberOfValues(Map<?, Set<T>> map) {
    int n = 0;
    for (Set<T> value : map.values()) {
        n += value.size();
    }
    return n;
}

Update: yet another way of achieving the same

public <T> int numberOfValues(Map<?, Set<T>> map) {
    int n = 0;
    for (Object key : map.keySet()) {
        n += map.get(key).size();
    }

    return n;
}

Final update: Thanks to Jorn’s answer, this is the final implementation...

public int numberOfValues(Map<?, ? extends Set<?>> map) {
    int n = 0;
    for (Set<?> value : map.values()) {
        n += value.size();
    }
    return n;
}
like image 373
Alf Avatar asked Nov 08 '16 14:11

Alf


1 Answers

You're missing the fact that Set<?> is also used as a generic parameter. And generics are invariant. i.e. when the parameter is Map<String, Set<?>>, the passed argument must be exactly Map<String, Set<?> (or a subtype of). Whereas with Set<V> the type argument is inferred.

You can solve this by using a bounded wildcard:

public <T> int numberOfValues(Map<T, ? extends Set<?>> map) {
    ...
}
like image 123
Jorn Vernee Avatar answered Sep 20 '22 17:09

Jorn Vernee