Alf Alf - 27 days ago 15
Java Question

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

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;
}

Answer

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) {
    ...
}
Comments