Страницы

Поиск по вопросам

четверг, 28 ноября 2019 г.

Final переменная для анонимного класса

#java


Есть пример кода:

public static void main(String[] args){
    int a=5;
    new Thread(new Runnable(){
        @Override
        public void run() {
            System.out.println(a);
        }
    }).start();
}


Нужно использовать переменную a в анонимном классе, компилятор говорит объявить её
как final.

Почему она должна быть final?
    


Ответы

Ответ 1



Это ограничение, которое ввели разработчики спецификации: при создании анонимного класса происходит захват объектов по значению, а не по ссылке. В анонимном классе создаются закрытые поля, в которые копируются значения переданных переменных. То есть по сути реализована эмуляция замыканий. Добавление модификатора final как бы говорит программисту о том, что при использовании такого рода замыканий не будут наблюдаться побочные эффекты (представьте, что вы передали в созданный объект анонимного класса переменную, а затем изменили её "снаружи" - вы в праве ожидать, что это изменение отразится и внутри замыкания, но это не так в силу пресловутой эмуляции; однако изменение внутреннего состояния mutable объектов будет видно и там и там). Для реализации полноценных замыканий необходимо расширять жизненный цикл захватываемых объектов (это касается объектов, расположенных в стеке, которые при выходе из области видимости, в которой они объявлены, уничтожаются). Почему это не реализовано до сих пор? Для такого рода реализации необходимо внести серьезное изменение в саму архитектуру JVM. Такого рода изменения достаточно сложно внедрить по определенным причинам: потребуется много усилий для внесения изменений не только со стороны Oracle, но и со стороны сторонних разработчиков (дебаггеры, обфускаторы, IDE, компиляторы и т.д.). Внесение такого изменения может привести к утрате обратной совместимости, что отразится на множестве уже существующих приложений. Изменение может отразиться на других языках/платформах, завязанных на среде JVM. В Java 8 ввели понятие effectively final - это переменные, которые не имеют модификатора final, но могут использоваться в анонимных классах и лямбда-выражениях. Переменная будет effectively final, если после её определения она не изменяется. Соответственно на Java 8 ваш код будет скомпилирован. Дополнительные ссылки для более подробного ознакомления: Accessing Members of an Enclosing Class, Why are only final variables accessible in anonymous class? Does Java 8 Support Closures?

Ответ 2



Ваш код создаёт поток, поток запускается на выполнение, а вызвавшая поток функция (в данном случае main) может завершиться. При завершении функции все её локальные переменные, в том числе и i исчезнут, выделенная под них память возвратится системе. Следовательно, поток не должен иметь доступ к локальным переменным. Но! Если у переменной Вы укажете квалификатор final, то Вы, фактически, говорите компилятору, что это не переменная, а константа. Компилятор будет уверен, что после инициализации эта константа не изменится, поэтому со спокойной совестью вместо прямого обращения к переменной поставит внутри кода потока её копию.

Комментариев нет:

Отправить комментарий