Why does 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 cannot call this passing to Map<String, Set<String>> . For example, the following does not compile:

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

Error message:

 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 go to the following, everything will be fine:

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

However, Im is not at all interested in V , since I just want to know the size of each of the sets.

For completeness, 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; } 

What Im knows is that this can also be done like this, but that is not a question :)

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

Update: another way to achieve 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 Jornes' 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; } 
+5
source share
2 answers

You are missing the fact that Set<?> also used as a generic parameter. And generics are invariant. that is, when the Map<String, Set<?>> parameter, the argument passed must be exactly Map<String, Set<?> (or a subtype). If using Set<V> type argument is output.

You can solve this problem with a limited template:

 public <T> int numberOfValues(Map<T, ? extends Set<?>> map) { ... } 
+10
source

Hint:

In your method numberOfValues it is completely legal to write something like

 Set<Object> set = new HashSet<>(); set.add(1); set.add("Powned"); map.put(null, set); 

You can replace null with a suitable key already on the map to make a valid display, completely violating the type safety in the surrounding code.

Either output or explicitly define the type of set, or add a wildcard binding to prevent access to map mutations (e.g. ? extends Set<?> )

0
source

Source: https://habr.com/ru/post/1259470/


All Articles