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

时间:2013-09-20 00:51:26

标签: java generics bounded-wildcard

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

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

编译器抱怨错误消息:

  

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

s的类型应该是什么? Eclipse建议Set<?>,但我试图让它更具体。

1 个答案:

答案 0 :(得分:5)

this old Apache thread

解决了这个问题
  

问题是entrySet()方法正在返回a   Set<Map.Entry<capture-of ? extends K, capture-of ? extends V>>,   这与类型Set<Map.Entry<? extends K, ? extends V>>不兼容。   如果我放弃extends Kextends V部分,则更容易描述原因。   我们有Set<Map.Entry<?, ?>Set<Map.Entry<capture-of ?, capture-of ?>>

     

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

     

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

问题的症结在于顶级通配符capture,这意味着它们本质上是一次性类型参数。相比之下,嵌套通配符不会捕获,并且具有某种不同的含义。

因此,为简单起见删除界限,声明

Map<?, ?> m;

表示“某些特定未知类型的键和某些特定未知类型值的映射”。

但宣布

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

表示“任何类型的键和值的一组条目”。

这就是你遇到麻烦的地方,因为表达式m.entrySet()不希望返回它,而是“一些特定未知类型的键和一些特定的未知类型的值“。这些类型不兼容,因为泛型aren't covariantSet<Type>不是Set<SuperType>

(请参阅这篇引人入胜的帖子,这有助于梳理嵌套通配符的细微差别:Multiple wildcards on a generic methods makes Java compiler (and me!) very confused。)

一种解决方法是使用capture helper方法,它利用了可以嵌套正式类型参数的事实:

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

这意味着不允许向null添加非s元素,但在Set返回entrySet的情况下,add无论如何都不支持addAll方法(感谢clarifying this point的newacct。)