Подтвердить что ты не робот

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.

Выполняет ли он код аналогично?

4b9b3361

Ответ 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, вам все равно нужно убедиться, что она никогда не переназначается.