有界通配符相关的编译器错误
我想知道这个代码有什么问题:
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 K
并extends 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);
这是一个人为的例子,因为String
和Integer
都是final
,但它显示了这个概念。
更简单的解决方法如下:
Set<? extends Map.Entry<? extends String, ? extends Integer>> s = m.entrySet();
这意味着不允许向s
添加非null
元素,但是在由entrySet
返回的Set
的情况下, add
和addAll
方法不受支持(感谢newacct 澄清这一点 )。