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

Видимость локальной переменной Java в анонимных внутренних классах - почему требуется ключевое слово "final"?

Я не понимаю, почему я не всегда могу получить доступ к переменной изнутри "слушателя" или "обработчика".

Это мой код:

Button btnDownload = new Button(myparent, SWT.NONE);
btnDownload.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                btnDownload.setEnabled(false); // I CAN'T 
            }
        });

Единственный способ - объявить его с помощью ключевого слова final:

final Button btnDownload = new Button(myparent, SWT.NONE);

Почему мне нужно объявить переменную final для получения доступа внутри события?

4b9b3361

Ответ 1

Ваш SelectionAdapter является анонимным внутренним классом, и я думаю, что это дает понять:

Локальные классы могут наиболее точно ссылаться на переменные экземпляра. Причина, по которой они не могут ссылаться на не конечные локальные переменные, заключается в том, что экземпляр локального класса может оставаться в памяти после возвращения метода. Когда метод возвращает локальные переменные, выходит за пределы области видимости, поэтому требуется их копирование. Если переменные werent final, то копия переменной в методе могла бы измениться, а копия в локальном классе didnt, поэтому они не синхронизированы.

Анонимные внутренние классы требуют конечных переменных из-за того, как они реализованы в Java. Анонимный внутренний класс (AIC) использует локальные переменные, создавая поле частного экземпляра, в котором хранится копия значения локальной переменной. Внутренний класс фактически не использует локальную переменную, а копию. На данном этапе должно быть довольно очевидно, что "Bad Thing" ™ может произойти, если изменяется исходное значение или скопированное значение; возникнут некоторые неожиданные проблемы синхронизации данных. Чтобы предотвратить такую ​​проблему, Java требует, чтобы вы отметили локальные переменные, которые будут использоваться AIC как окончательные (то есть, неизменяемые). Это гарантирует, что внутренние копии экземпляров локальных переменных всегда будут соответствовать фактическим значениям.

Ответ 2

Я считаю, что Tom говорит, что если вы можете использовать локальные переменные в анонимном классе, то какую кнопку он должен включить в следующем сегменте кода.

    Button btnDownload = new Button(myparent, SWT.NONE);
    btnDownload.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    btnDownload.setEnabled(false); // I CAN'T 
                }
            });
   btnDownload = new Button(somethingelse , SWT.NONE);
   btnDownload = null ;

Создатели java хотели избежать этой дискуссии, требуя, чтобы локальные переменные, используемые в анонимных классах, были окончательными.

Ответ 3

Вероятно, это не оптимальный выбор дизайна. В Java 8, наряду с выражением лямбда, это требование final, надеюсь, будет удалено.

Цель состоит в том, чтобы запретить присвоение локальной переменной из анонимного класса. Но это не требует обозначения локальной переменной как окончательной.

void f()

  Object var = ...;
  new Anon(){
      ...
      print(var); // reading is ok
      var = x;    // error, can't write to var            [1]
  }

Компилятор фактически создает копию var и сохраняет ее в анонимном классе. Анонимный класс получает доступ только к копии. Приведенный выше код фактически преобразуется в

void f()

  Object var = ...;
  new Anon$1(var);

class Anon$1
{
    final Object $var;
    Anon$1(Object var){ this.$var=var; }
    ...
    print($var); 
    $var = x;    // error, naturally                      [2]
}

Как вы можете видеть, нет никаких технических причин требовать, чтобы var был окончательным. Весь компилятор должен делать, когда он сталкивается с [2], зная, что $var является синтезированным полем для var, сообщает об ошибке "Локальная переменная var не может быть назначена анонимным классом" (вместо "$ var является окончательной и не может быть назначено на" )

Разработчики языка решили раздражать нас, требуя ключевое слово final для локальной переменной; Я не помню логики; в общем, Java не боялась многословия, если требуется ясность.