Видимость локальной переменной Java в анонимных внутренних классах - почему требуется ключевое слово 'final'? - PullRequest
24 голосов
/ 15 сентября 2011

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

Это мой код:

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 , чтобы получить доступ внутри события?

Ответы [ 4 ]

38 голосов
/ 15 сентября 2011

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

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

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

17 голосов
/ 15 сентября 2011

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

    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 голосов
/ 15 сентября 2011

Вероятно, это не оптимальный выбор дизайна. В 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 is final») и не может быть назначен на ")

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

0 голосов
/ 08 ноября 2014

Этот пост объясняет, почему

Переменные метода Java и анонимный класс

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...