Почему в нем говорится, что "Нельзя ссылаться на не конечную переменную я внутри внутреннего класса, определенного другим методом"?
У меня есть кнопка для прослушивания кнопок и в методе onCreate()
у меня есть локальная переменная типа
onCreate() {
super.onCreate();
int i = 10;
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
i++;
}
});
Почему java просит меня сделать окончательный?
Ответы
Ответ 1
Когда метод onCreate() вернется, ваша локальная переменная будет очищена из стека, поэтому они больше не будут существовать. Но анонимный объект класса new View.OnClickListener() ссылается на эти переменные. Из-за этого неправильное поведение, поэтому java не позволяет вам это делать.
После того, как он окончательный, он становится константой. Таким образом, он хранится в куче и может быть безопасно использован в анонимных классах.
Ответ 2
Ваш анонимный внутренний класс ссылается на свою охватывающую область, беря копии локальных переменных - если вы хотите изменить значение int в анонимном внутреннем классе, вам нужно сделать хакерство:
final int[] arr = new int[1];
arr[0] = 10;
Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
arr[0]++;
}
Ответ 3
Потому что вы обращаетесь к нему внутри анонимного внутреннего класса. Вы не можете этого сделать. Вы можете сделать это окончательным, а затем прочитать его из анонимного внутреннего класса, но вы не сможете его увеличить.
Параметры:
- Вместо этого сделайте переменную экземпляра внешнего класса
- Вместо этого сделайте переменную экземпляра анонимного внутреннего класса
- Используйте обертку - например. одноэлементный массив,
AtomicInteger
или что-то вроде этого
Я бы предпочел бы второй вариант, если мне не понадобилось i
в любом месте. Если честно, я считаю, что третий вариант - это немного неприятный хак.
Ответ 4
Создание переменной final
необходимо, потому что под капотом анонимные внутренние классы вроде этого синтаксический сахар, которые скомпилируются в вложенный класс вне рамки содержащего метода.
Это означает, что ни одна из переменных, объявленных внутри метода, не доступна для внутреннего класса, поэтому компилятор извлекает другой трюк - и копирует значение в скрытом конструкторе для вашего класса. Чтобы избежать путаницы программиста, когда эта копия не обновляется, чтобы соответствовать изменениям в переменной в методе, она должна быть окончательной, чтобы убедиться, что таких изменений нет.
Так как ваша цель состоит в том, чтобы иметь инкрементирующее целое число, и только ссылка должна быть окончательной (сам объект не должен быть неизменным), вы можете объявить final AtomicInteger i
, а затем увеличивать его, как вы хотите, от обратного вызова.
Ответ 5
Поскольку вам нужно увеличивать переменную i
, вы не можете сделать ее окончательной. Вместо этого вы можете сделать его членом класса.