【发布时间】:2015-01-10 20:40:47
【问题描述】:
以下类有一个名为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);
示例代码:
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);
}
}};
}
}
【问题讨论】:
-
@tobias_k 这不是真正的双花括号。有一对大括号用于创建匿名内部类,另一对用于实例初始化程序块。有些人喜欢将其格式化为双花括号,但实际上并没有什么特殊含义。
-
@tobias_k 是的,实际上初始化程序块被复制到每个构造函数的开头。 (如果有多个块,则按照它们在源代码中出现的顺序执行。)
-
顺便说一句,“花括号”这个词是我最讨厌的。它们只是“大括号”。
-
使用
entries.stream().collect(toMap(Entry::getName, e->e))(静态导入Collectors.toMap)而不是“双括号初始化”,这很糟糕。 -
问题在于它捕获了对封闭类的引用,从而防止在地图存在时对其进行垃圾收集。当然,这在某个地方可能还不错,但是在每次实例化这样的地图之前,您是否会停下来考虑一下?在将一些代码移动到不同的类之前,您项目中的每个开发人员都会考虑这一点吗?在添加大量状态之前,他们是否都会检查类中的双括号习语?我会避免这个成语。还有很多更好的方法。
标签: java java-8 inner-classes shadowing