Прочитайте эту статью , чтобы понять детали реализации:
причина этого ограничения
становится очевидным, если мы пролить свет
о том, как реализованы локальные классы.
Анонимный локальный класс может использовать локальный
переменные, потому что компилятор
автоматически дает классу
поле частного экземпляра для хранения копии
каждой локальной переменной использует класс.
Компилятор также добавляет скрытые
параметры для каждого конструктора
инициализировать эти автоматически созданные
частные поля. Таким образом, местный класс
на самом деле не доступ к локальным
переменные, но просто свой собственный
их копии. Единственный способ, которым это может
работать правильно, если местный
переменные объявляются окончательными, так что
они гарантированно не изменятся.
С этой гарантией на месте,
местный класс уверен, что его
внутренние копии переменных
точно отражать фактическое местное
переменные.
EDIT:
Берлин Браун говорит: «Я опубликовал декомпилированную версию
анонимный внутренний класс. Но быть
честно, я до сих пор не понимаю, почему
Компилятор должен иметь эту информацию.
Даже если поле объявлено окончательным,
поле все еще может быть нулевым. Я думаю
это одна из тех причуд Java, вы
должен объявить это поле
окончательно ... потому что так оно и есть.
Нет четкой причины, почему "
Причина в том, чтобы убедиться, что пользователи понимают, что замыкания «закрывают» переменные, а не значения. Давайте предположим, что не было необходимости иметь конечные локальные переменные. Тогда мы могли бы написать код вроде:
public void doIt() {
for(int i = 0; i < 3; ++i) {
runnables.add(new Runnable() {
@Override
public void run() {
System.out.println(i);
}
});
}
run(runnables); // run each runnable
}
Как вы думаете, что будет выход? Если вы думаете, что это будет «0 1 2», вы ошибетесь, поскольку Runnable закрывает «переменную» i, а не «значение» i в тот момент времени, и, следовательно, на выходе будет «2 2 2». Что можно сделать для достижения ожидаемого поведения здесь? Два решения: либо полагаться на то, что пользователи понимают, как работают замыкания, либо каким-либо образом применять его на уровне языка. И это второй вариант, с которым столкнулись разработчики языка.
public void doIt() {
for(int i = 0; i < 3; ++i) {
final int j = i; // notice the final local variable
runnables.add(new Runnable() {
@Override
public void run() {
System.out.println(j);
}
});
}
run(runnables);
}
JFTR, я не говорю, что второй вариант - это «подходящий» путь, просто то, что локальные переменные, помеченные как final, перед использованием в анонимных внутренних классах, для меня являются серьезной проблемой. Конечно, YMMV. : -)