使用 Map.entrySet() 时类型不匹配

Type mismatch when using Map.entrySet()

我遇到以下情况:在下面的代码中,方法 foo 可以编译,而方法 bar 不会。在方法调用 entrySet(在代码中指示)时,编译器说:

Type mismatch: cannot convert 
from Set<Map.Entry<capture#1-of ? extends K,capture#2-of ? extends V>> 
to Set<Map.Entry<? extends K,? extends V>>

有趣的是,Eclipse 的快速修复建议

Change type of 's' to Set<Entry<? extends K, ? extends V>>

它只更改了代码,因为 quickfix 忽略了它自己的建议,而是将 s 的类型更改为 Set<?>

我正在使用 JDK1.8.0_51 和 Eclipse 4.4.0。也许它与通配符或捕获有关?任何帮助或建议将不胜感激。提前致谢!

import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

public class MyClass<K, V> {
    public void foo(Set<Entry<? extends K, ? extends V>> set) {
        Iterator<Entry<? extends K, ? extends V>> i = set.iterator();
    }

    public void bar(Map<? extends K, ? extends V> map) {
        Set<Entry<? extends K, ? extends V>> s = map.entrySet();
                                                 ^^^^^^^^^^^^^^
    }
}

假设 K 和 V 是数字。该声明不会 link 随映射传入的类型到条目集中使用的类型。虽然我们知道它永远不会发生,但如果 map 是 Map<Integer,Integer> 那么声明允许 s 是 Set<Entry<Double,Double>> 因为它仍然扩展 Number.

因此,如果您明确表示这些类型匹配,可以这样写:

public <K0 extends K, V0 extends V> void bar(Map<K0,V0> map) {
    Set<Entry<K0,V0>> s = map.entrySet();
}

您的意思很明确,'s' 的类型将与 'map' 的类型完全匹配。因此它编译愉快。

简短的回答是,如果您按照问题中的方式声明 Set,则可以向其中添加不符合传递给方法的对象类型的条目。 Java 没有保留足够的信息来检查 Set 定义中的“?extends K”是否与方法参数中的“?extends K”相同。

为避免这种情况 Java 要求您将分配声明为:

Set<? extends Map.Entry<? extends K,? extends V>> s = map.entrySet();

...您会发现您无法将自己的条目添加到此集合中 - 至少,如果不进行大量错误转换,则会产生大量警告。

正如上面提到的那样,这个问题更详细地涵盖了主题:Generic Iterator on Entry Set