#lambda #java-8 #javac
#лямбда #java-8 #javac
Вопрос:
Следующий класс содержит переменную-член runnable
, которая инициализируется экземпляром анонимного внутреннего класса. Внутренний класс ссылается на тот же член:
class Example {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(runnable);
}
};
}
Это не проблема, если метод не выполняется до того, как член был назначен, и JLS разрешает такую ссылку.
Объявление переменной-члена теоретически может быть преобразовано в лямбда-выражение, подобное этому:
Runnable runnable = () -> System.out.println(runnable);
Насколько я понимаю, это функционально эквивалентно предыдущему примеру, но оно отклоняется javac 1.8.0_05
со следующим сообщением об ошибке:
Error:(2, 54) java: self-reference in initializer
Хотя это утверждение верно, я не понимаю, почему это было запрещено. Было ли это намеренно запрещено, возможно, потому, что лямбда-выражения компилируются в другой байтовый код, что привело бы к проблемам, если бы это было разрешено? Или был просто запрещен, потому что уже были проблемы с этими ссылками, когда они использовались в анонимных внутренних классах? Или это было непреднамеренно запрещено авторами JLS? Или это ошибка в javac
?
Комментарии:
1. В eclipse это работает:
Runnable runnable = () -> System.out.println(this.runnable);
Это происходит только без добавленногоthis
квалификатора.
Ответ №1:
Ошибка #JDK-8027941 описывает именно это. Дэн Смит (руководитель спецификации проекта Lambda) пишет, что это не ошибка и не ограничивается лямбдами.
В комментариях к соответствующему отчету об ошибке он формулирует это так:
8.3.2.3: Во-первых, «использование» поля в инициализаторе поля обычно запрещено, если использование происходит до объявления поля. Спецификация не очень ясна в этом вопросе, но намерение всегда заключалось в том, что «до» включает собственный инициализатор поля. Итак, «
int x = x 1;
» не является допустимым объявлением поля.
Он также отмечает:
Можно было бы добавить функцию, которая обрабатывала бы лямбда-тела особым образом, например, тела анонимных классов (или, в более общем плане, позволяла лямбде ссылаться на себя, если это инициализатор переменной), но этого не было сделано. (FWIW, простая настройка 8.3.2.3 была бы не совсем безопасной, точно так же, как 4-й пункт в настоящее время не совсем безопасен: «
Function f = (Function) ((Function) e -> f.apply(e)).apply(null);
«.)
Я думаю, проблема в том, что разработчики Java хотят иметь простые синтаксические правила, чтобы решать, какие операторы разрешены, а не зависеть от более сложного семантического анализа кода. Преимущество, вероятно, заключается в более простой спецификации и, следовательно, меньших требованиях к компиляторам, в то время как стоимость заключается в том, что программисты не могут выразить каждую программу — по крайней мере, не так, как они хотят.
Как указывает Марко Топольник, есть решение: полностью квалифицировать поле. Пример из отчета об ошибке:
import java.util.function.Function;
public class LambdaSelfRef {
// COMPILATION FAILURE
public static Function<Object, Object> op1 = e -> op1.apply(e);
// COMPILES OK
public static Function<Object, Object> op2 = e -> LambdaSelfRef.op2.apply(e);
/* ... */
}