使用泛型返回类型提取表达式后键入不匹配

Mar*_*cus 5 java generics refactoring types capture

在执行提取表达式重构时,我偶然发现了Eclipse 4.4和Java 8 build 45中的一些奇怪的行为.以下示例显示了应用提取重构之前的原始和无错代码:

import java.util.Map;
import java.util.Set;

public class MyMap<K, V> {
    public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
        for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) {
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Eclipse重构的结果如下所示,并导致下面的错误消息引用entrySet循环声明中的读取访问:

    public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
        Set<?> entrySet = mapToCopy.entrySet();
        for (Map.Entry<? extends K, ? extends V> entry : entrySet) {
                                                         ^^^^^^^^
        }
    }

Type mismatch: cannot convert 
    from element type capture#3-of ? 
    to Map.Entry<? extends K,? extends V>
Run Code Online (Sandbox Code Playgroud)

我改变的声明的类型entrySetSet<Map.Entry<? extends K, ? extends V>>.这次,错误在声明的初始化程序中指出,说:

    public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
        Set<Map.Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet(); 
                                                            ^^^^^^^^^^^^^^^^^^^^
        for (Map.Entry<? extends K, ? extends V> entry : 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>>
Run Code Online (Sandbox Code Playgroud)

由于原始代码编译,我有点困惑.也许有人可以帮助我并给出解释?提前致谢!

Kon*_*kov 0

我们先回顾一下原始出处:

public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
    for (Map.Entry<? extends K, ? extends V> entry : mapToCopy.entrySet()) {
    }
}
Run Code Online (Sandbox Code Playgroud)

在内部(和运行时),这将被编译并工作为:

public void putAll(final Map mapToCopy) {
    for (Iterator<Map.Entry> iterator = mapToCopy.iterator; iterator.hasNext();) {
    }
}
Run Code Online (Sandbox Code Playgroud)

其中? extends K? extends V将在类型擦除后替换为一些真实类型。编译器会知道这些类型是什么,并且不会引发Exception类型不兼容性。

另一方面,如果你将源重构为此,

public void putAll(final Map<? extends K, ? extends V> mapToCopy) {
    Set<Entry<? extends K, ? extends V>> entrySet = mapToCopy.entrySet();
                                                    ^^^^^^^^
    for (Map.Entry<? extends K, ? extends V> entry : entrySet) {

    }
}
Run Code Online (Sandbox Code Playgroud)

那么编译器将没有证据表明 与entrySet 具有相同的类型Map.Entry<? extends K, ? extends V>,仅仅是因为通配符 ( ?) 始终代表未知的东西,即不能保证 的entrySet条目键值中的通配符将与 的entry键相同值(来自循环)。由于不能百分百确定类型兼容,编译器会引发编译时错误,**即使**您可能确信这些类型在运行时是相同的。