Инициализация двойной фигурной скобки 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 и описывает то, что не идеально.