Java 8双花括号初始化和名称冲突

Bri*_*ggs 19 java inner-classes shadowing java-8

以下类有一个名为的内部类Entry.此代码不会在Java 8中编译,因为编译器假定Entry双花括号初始值设定项中的引用是类型Map.Entry而不是Scope.Entry.这段代码在JDK的先前版本(至少6和7)中编译,但在JDK 8中被破坏.我的问题是"为什么?" Map.Entry未在此类中导入,因此编译器没有理由假设该值是类型Map.Entry.是否有一些隐含的范围被引入或匿名类的东西?

错误:

scope/Scope.java:23: error: incompatible types: scope.Scope.Entry cannot be converted to java.util.Map.Entry for (final Entry entry : entries) {
scope/Scope.java:22: error: cannot find symbol put(entry.getName(), entry);
Run Code Online (Sandbox Code Playgroud)

示例代码:

package scope;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

public class Scope {

    public static class Entry<T> {
        public String getName() {
            return "Scope";
        }
    }

    public static void main(String[] args) {
        final Set<Entry> entries = new HashSet<>();

        new HashMap<String, Entry>() {{
            // Why does the Java 8 compiler assume this is a Map.Entry
            // as it is not imported? 
            for (final Entry entry : entries) {
                put(entry.getName(), entry);
            }
        }};
    }
}
Run Code Online (Sandbox Code Playgroud)

Flo*_*oky 27

它绝对不是一个bug,它是使用双括号初始化的副作用.

new HashMap<String, Entry>() {{
    for (final Entry entry : entries) {
        put(entry.getName(), entry);
    }
}};
Run Code Online (Sandbox Code Playgroud)

这种类型的初始化基本上是滥用实例初始化块的一种聪明方法.它使用初始化块创建HashMap的匿名子类,然后在调用之前将该块复制到其默认构造函数的开头.此子类优先考虑其父级范围内的Entry,而不是嵌套的范围.这可以通过阴影来解释.

8.1.6开始.类主体和成员声明

如果C本身是嵌套类,则在封闭范围中可能存在相同类型(变量,方法或类型)和名称的定义.(范围可以是块,类或包.)在所有这些情况下,成员m在C shadow(第6.4.1节)中声明或继承了相同类型和名称的其他定义.[强调我的]

这里C是声明的匿名内部类.由于它继承HashMap,java.util.Map.Entry阴影scope.Scope.Entry.

至于它为什么要编译你想要的以前的版本,我不知道.这些行为存在于那些版本中(我引用的文档来自7),因此它不应该有效.也许这些版本可能会被窃听.