19

下面的类有一个名为 的内部类Entry。此代码不会在 Java 8 中编译,因为编译器假定Entry双花括号初始值设定项中的引用是 typeMap.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);

示例代码:

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);
            }
        }};
    }
}
4

2 回答 2

27

这绝对不是错误,而是使用双括号初始化的副作用。

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

这种类型的初始化基本上是一种滥用实例初始化块的聪明方法。它使用初始化块创建 HashMap 的匿名子类,然后在调用它之前将该块复制到其默认构造函数的开头。这个子类在它的父类的范围内赋予条目优先级,而不是在它嵌套的范围内。这由shadowing解释。

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

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

这里,C是声明的匿名内部类。既然继承自HashMap, java.util.Map.Entryshadows scope.Scope.Entry

至于为什么它与以前的版本一样编译,我不知道。这种行为出现在那些版本中(我引用的文档来自7),所以它不应该起作用。所以也许这些版本有问题。

于 2014-11-13T18:53:11.677 回答
0

类型成员的范围和阴影对于编译器来说是一个困难的地方。有很多与此相关的错误 - 主要是关于嵌套/内部/匿名类型。我找不到与该问题完全相关的问题,但我知道一些可能与之相关的问题。是一个与此非常相似的情况(类型变量而不是封闭类型)。

关于什么规范说关于阴影也有一个问题。它引用了 JLS 并描述了不理想的地方。

于 2014-11-13T22:45:31.173 回答