Ссылка на конечное поле из лямбда-выражения

Недавно я обнаружил тонкую разницу между анонимным классом и выражением лямбда:

public class FinalTest {
    final Runnable x = new Runnable() {
        @Override
        public void run() {
            System.out.println(x.hashCode());
        }
    };

    final Runnable y = () -> System.out.println(y.hashCode()); 
}

Обычно lambdas эквивалентны анонимным классам. Даже у моей Eclipse IDE есть рефакторинг для преобразования x в лямбда (он становится точно как y) и конвертирует y в анонимный класс (он становится точно таким же, как x). Однако lambda дает мне ошибку компиляции, в то время как анонимный класс может быть отлично скомпилирован. Сообщение об ошибке выглядит следующим образом:

>javac FinalTest.java
FinalTest.java:9: error: self-reference in initializer
    final Runnable y = () -> System.out.println(y.hashCode());
                                                ^
1 error

Итак, возникает вопрос: почему существует такая разница?

Ответы

Ответ 1

Это связано с JLS # 8.3.3, касающимся прямых ссылок. В частности, если вы используете полное имя, которое оно компилирует (поскольку третье условие этого правила становится ложным). Использование - простое имя либо в инициализаторе переменной экземпляра C, либо в инициализаторе экземпляра C):

final Runnable y = () -> System.out.println(this.y.hashCode());

В случае анонимного класса четвертое условие (C является самым внутренним классом или интерфейсом, охватывающим использование) не является истинным, потому что охватывающий класс является самим анонимным классом.