为什么 Map<String, Set<String>> 不匹配 Map<T, Set<?>>?

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

我有一个具有以下签名的方法:

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

但是我不能调用它传递一个Map<String, Set<String>>。例如,以下不会编译:

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

错误信息是:

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>>)

但是,如果我更改为以下一切都很好:

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

但是我对V一点都不感兴趣,因为我只想知道每个集合的大小。

为了完整起见,这是整个方法:

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

我知道它也可以这样完成,但这不是问题的重点:)

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

更新: 实现相同目标的另一种方式

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

    return n;
}

最终更新: 感谢 Jorn 的回答,这是最终的实现...

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

您忽略了 Set<?> 也用作通用参数这一事实。泛型是不变的。即,当参数为 Map<String, Set<?>> 时,传递的参数必须恰好为 Map<String, Set<?>(或其子类型)。而使用 Set<V> 类型参数是推断出来的。

您可以使用有界通配符解决此问题:

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

提示:

在你的 numberOfValues 方法中,写

这样的东西是完全合法的
Set<Object> set = new HashSet<>();
set.add(1);
set.add("Powned");
map.put(null, set);

您可以将 null 替换为映射中已有的合适键以进行有效映射,从而完全破坏周围代码中的类型安全。

推断或显式定义集合的类型,或添加通配符绑定以防止更改映射访问(如 ? extends Set<?>