Ссылка на конечное поле из лямбда-выражения
Недавно я обнаружил тонкую разницу между анонимным классом и выражением лямбда:
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 является самым внутренним классом или интерфейсом, охватывающим использование) не является истинным, потому что охватывающий класс является самим анонимным классом.