Инициализация двойной фигурной скобки Java 8 и столкновение имен
следующий класс имеет внутренний класс под названием Entry
. Этот код не будет компилироваться в Java 8, поскольку компилятор предполагает Entry
ссылка в инициализаторе двойной фигурной скобки имеет тип Map.Entry
, а не Scope.Entry
. Этот код компилируется в предыдущих версиях (по крайней мере, 6 и 7) JDK, но разбивается в 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);
}
}};
}
}
2 ответов
это определенно не ошибка, это побочный эффект использования инициализации двойной скобки.
new HashMap<String, Entry>() {{
for (final Entry entry : entries) {
put(entry.getName(), entry);
}
}};
этот тип инициализации в основном умный способ злоупотреблять инициализации экземпляра блока. Он создает анонимный подкласс HashMap с блоком инициализации, а затем копирует этот блок в начало конструктора по умолчанию перед его вызовом. Этот подкласс предоставляет приоритет записи в области своего родителя, а не области, которая он вложен. Это объясняется слежка.
С 8.1.6. Объявления тела класса и членов
если сам C является вложенным классом, могут быть определения того же самого вид (переменная, метод или тип) и имя как m в охватывающих областях. (Области могут быть блоками, классами или пакетами.) Во всех таких случаях, член m объявлен в или наследуется C тени (§6.4.1) другой определения тот же вид и имя. [выделено мной]
здесь C
объявлен анонимный внутренний класс. Поскольку он наследуется от HashMap
, java.util.Map.Entry
тени scope.Scope.Entry
.
что касается того, почему он скомпилирован так, как вы хотели с предыдущими версиями, я понятия не имею. Это поведение присутствовало в этих версиях (документы, на которые я ссылался, из 7
), так что это не должно было сработать. Так что, возможно, эти версии прослушиваются.
области элементов типа и затенение-трудное место для компилятора. Было / есть количество ошибок, связанных с этим - в основном о вложенных/внутренних/анонимных типах. Я не могу найти ту, которая ровно об этой проблеме, но я знаю некоторые, которые могут быть связаны с ней. здесь - это случай, очень похожий на этот (переменная типа вместо заключительного типа).
относительно того, что спецификация говорит о затенении, также есть вопрос. Он имеет ссылки на JLS и описывает то, что не идеально.