有界通配符相关的编译器错误

我想知道这个代码有什么问题:

Map <? extends String, ? extends Integer> m = null; Set<Map.Entry<? extends String, ? extends Integer>> s = m.entrySet(); 

编译器抱怨错误信息:

types不匹配:无法从Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>> Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>> Set<Map.Entry<capture#1-of ? extends String,capture#2-of ? extends Integer>> Set<Map.Entry<? extends String,? extends Integer>> Set<Map.Entry<? extends String,? extends Integer>>

应该是什么types的? Eclipsebuild议Set<?>但是我想要比这更具体。

这个旧的Apache线程解决了这个问题:

问题是entrySet()方法正在返回一个Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>> ,这与Set<Map.Entry<? extends K, ? extends V>>types不兼容Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> Set<Map.Entry<? extends K, ? extends V>> 。 如果我放弃extends Kextends V部分, extends V容易描述。 所以我们有Set<Map.Entry<?, ?>Set<Map.Entry<capture-of ?, capture-of ?>>

第一个, Set<Map.Entry<?, ?>>是一组不同types的Map.Entries,即它是一个异构集合。 它可以包含一个Map.Entry<Long, Date>和一个Map.Entry<String, ResultSet>>和任何其他types的对,它们都在同一个集合中。

另一方面, Set<Map.Entry<capture-of ?, capture-of ?>>是相同(尽pipe未知)types的同类集合。 例如,它可能是一个Set<Map.Entry<Long, Date>> ,所以集合中的所有条目必须是Map.Entry<Long, Date>

问题的关键是捕获顶级通配符,这意味着它们实质上是一次性types参数。 相反,嵌套通配符不捕获,并且具有不同的含义。

所以,为了简单起见,删除了界限,声明

 Map<?, ?> m; 

意思是“一些特定的未知types的键和一些特定的未知types的值的地图”。

但是宣布

 Set<Map.Entry<?, ?>> s; 

意思是“任何types的键和值的一组条目”。

所以这就是你遇到麻烦的地方,因为expression式m.entrySet()不想返回,而是“一些特定的未知types的键和一些特定的未知types的值的一组条目”。 而这些types是不兼容的,因为generics不是协变的 : Set<Type>不是Set<SuperType>

(请参阅这个迷人的post,它有助于区分嵌套通配符的细微差别: 通用方法上的多个通配符使Java编译器(和我!)非常困惑 。

一种解决方法是使用捕获帮助器方法,该方法利用了正式types参数可以嵌套的事实:

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

这是一个人为的例子,因为StringInteger都是final ,但它显示了这个概念。

更简单的解决方法如下:

 Set<? extends Map.Entry<? extends String, ? extends Integer>> s = m.entrySet(); 

这意味着不允许向s添加非null元素,但是在由entrySet返回的Set的情况下, addaddAll方法不受支持(感谢newacct 澄清这一点 )。