Java перестала ошибочно проверять не конечные переменные во внутренних классах (java 8)
В Java 7 говорилось: "Невозможно ссылаться на неточное сообщение локальной переменной, определенное в охватывающей области" по следующему коду:
public class Runner {
public static void main(String[] args) {
String message = "Hello world";
new Runnable() {
@Override
public void run() {
System.out.println(message);
}
}.run();
}
}
Java 8 не работает.
Подозреваю, что речь идет о добавлении функций функционального программирования в Java.
Выполняет ли он код аналогично?
Ответы
Ответ 1
Java 8 неявно делает message
final, потому что она никогда не изменяется. Попробуйте изменить его в любом месте вашего кода, и вы получите ошибку компиляции (потому что это удаляет неявный final
).
Это называется эффективным окончательным. Цитирование Из документов:
Однако, начиная с Java SE 8, локальный класс может обращаться к локальным переменным и параметрам закрывающего блока, которые являются окончательными или фактически окончательными. Переменная или параметр, значение которого никогда не изменяется после его инициализации, фактически окончательно.
Ответ 2
Java 8 (и Lambdas) представляет эффективный окончательный термин: даже если вы не занимались окончательным с ключевым словом final
, если он не изменен, он также хорош как окончательный.
Цитата из Учебное пособие Oracle: локальные классы:
Однако, начиная с Java SE 8, локальный класс может обращаться к локальным переменным и параметрам закрывающего блока, которые являются окончательными или фактически окончательными. Переменная или параметр, значение которого никогда не изменяется после его инициализации, фактически окончательно.
Ваше сообщение действительно окончательно, поэтому оно относится к нему из анонимных внутренних классов и lambdas.
Если вы измените значение сообщения, оно больше не будет окончательно окончательным:
String message = "Hello world";
new Runnable() {
@Override
public void run() {
System.out.println(message);
}
}.run();
message = "modified";
И поэтому вы получаете следующую ошибку (из Eclipse):
Локальное сообщение переменной, заданное в охватывающей области, должно быть окончательным или эффективно окончательным
Или форма javac
:
ошибка: локальные переменные, на которые ссылается внутренний класс, должны быть окончательными или фактически окончательными
Ответ 3
Переменная message
эффективно завершена. Цитата из справочной системы языка
Если переменная является фактически окончательной, добавление окончательного модификатора к ее декларация не будет вводить какие-либо ошибки времени компиляции.
Следовательно, поскольку ссылка message
не изменяется нигде внутри вашего внутреннего класса, компилятор рассматривает ее как окончательную.
Это вызовет ошибку:
new Runnable() {
@Override
public void run() {
message = "hey";
System.out.println(message);
}
}.run();
Причина, ошибка компилятора java7 вызывает ошибку из-за изменения спецификации для lambdas.
Используется любая локальная переменная, формальный параметр или параметр исключения, но не используется ни один параметр локальной переменной, формальный параметр или исключение но не объявленные в выражении лямбда, либо должны быть объявлены окончательными или быть фактически окончательным (§4.12.4), или возникает ошибка времени компиляции где используется попытка.
Анонимные внутренние классы и lambdas используют одни и те же правила.
Ответ 4
Да, это своего рода.
В основном они поняли, что компилятор уже должен решить, проанализировав код, когда локальная переменная "эффективно окончательна", то есть ее значение никогда не изменяется.
Итак, семантика не изменилась: пока нет необходимости явно объявлять переменную final
, вам все равно нужно убедиться, что она никогда не переназначается.