这个问题在这个旧的 Apache 线程中得到了解决:
问题是该entrySet()
方法返回一个
Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>>
与类型不兼容的Set<Map.Entry<? extends K, ? extends V>>
。更容易描述为什么如果我放弃extends K
andextends V
部分。所以我们有Set<Map.Entry<?, ?>
和Set<Map.Entry<capture-of ?, capture-of ?>>
。
第一个Set<Map.Entry<?, ?>>
是一组不同类型的 Map.Entries - 即它是一个异构集合。它可以包含 a
Map.Entry<Long, Date>
和 aMap.Entry<String, ResultSet>>
以及任何其他类型对,都在同一个集合中。
另一方面,Set<Map.Entry<capture-of ?, capture-of ?>>
是相同(尽管未知)类型对的同质集合。例如,它可能是一个
Set<Map.Entry<Long, Date>>
,所以集合中的所有条目都必须是
Map.Entry<Long, Date>
。
问题的症结在于顶级通配符捕获,这意味着它们本质上是一次性类型参数。相比之下,嵌套通配符不捕获,并且具有某种不同的含义。
因此,为了简单起见,删除界限,声明
Map<?, ?> m;
意思是“一些特定的未知类型的键和一些特定的未知类型的值的映射”。
但是声明
Set<Map.Entry<?, ?>> s;
意思是“一组任何类型的键和值的条目”。
所以这就是你遇到麻烦的地方,因为表达式m.entrySet()
不想返回它,而是“一组特定的未知类型的键和一些特定的未知类型的值的条目”。这些类型是不兼容的,因为泛型不是协变的: ASet<Type>
不是 a Set<SuperType>
。
(请参阅这篇引人入胜的帖子,它有助于梳理嵌套通配符的细微差别:泛型方法上的多个通配符使 Java 编译器(和我!)非常困惑。)
一种解决方法是使用捕获辅助方法,它利用了可以嵌套形式类型参数的事实:
private <K extends String, V extends Integer> void help(final Map<K, V> map) {
final Set<Map.Entry<K, V>> entries = map.entrySet();
// logic
}
...
Map<? extends String, ? extends Integer> m = null;
help(m);
这是一个人为的例子,因为String
and Integer
are both final
,但它显示了这个概念。
一个更简单的解决方法如下:
Set<? extends Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
这意味着不允许添加非null
元素到,但在返回的情况下,无论如何都不支持and方法(感谢 newacct澄清这一点)。s
Set
entrySet
add
addAll