Вопрос-ответ

Cannot refer to a non-final variable inside an inner class defined in a different method

Невозможно ссылаться на неокончательную переменную внутри внутреннего класса, определенного другим методом

Отредактировано: мне нужно изменить значения нескольких переменных, поскольку они выполняются несколько раз по таймеру. Мне нужно постоянно обновлять значения с каждой итерацией по таймеру. Я не могу установить значения в final, поскольку это помешает мне обновить значения, однако я получаю ошибку, которую я описываю в начальном вопросе ниже:

Я ранее написал то, что приведено ниже:


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


Это происходит для дважды вызываемой price и Price, называемой priceObject . Вы знаете, почему у меня возникает эта проблема. Я не понимаю, зачем мне нужно иметь окончательное объявление. Также, если вы видите, что я пытаюсь сделать, что мне нужно сделать, чтобы обойти эту проблему.


public static void main(String args[]) {

int period = 2000;
int delay = 2000;

double lastPrice = 0;
Price priceObject = new Price();
double price = 0;

Timer timer = new Timer();

timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
price = priceObject.getNextPrice(lastPrice);
System.out.println();
lastPrice = price;
}
}, delay, period);
}
Переведено автоматически
Ответ 1

Java не поддерживает истинные замыкания, хотя использование анонимного класса, подобного тому, который вы используете здесь (new TimerTask() { ... }), выглядит как своего рода замыкание.

редактировать - Смотрите комментарии ниже - следующее неверное объяснение, как указывает KeeperOfTheSoul.

Вот почему это не работает:

Переменные lastPrice и price являются локальными переменными в методе main() . Объект, который вы создаете с помощью анонимного класса, может сохраняться до тех пор, пока main() метод не вернет результат.

Когда main() метод возвращает значения, локальные переменные (такие как lastPrice и price) будут удалены из стека, поэтому после main() возврата они больше не будут существовать.

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

Создавая lastPrice и price final, они на самом деле больше не переменные, а константы. Затем компилятор может просто заменить использование lastPrice и price в анонимном классе значениями констант (во время компиляции, конечно), и у вас больше не будет проблем с доступом к несуществующим переменным.

Другие языки программирования, которые поддерживают замыкания, делают это, обрабатывая эти переменные особым образом - следя за тем, чтобы они не уничтожались при завершении метода, чтобы замыкание все еще могло получить доступ к переменным.

@Ankur: Вы могли бы сделать это:

public static void main(String args[]) {
int period = 2000;
int delay = 2000;

Timer timer = new Timer();

timer.scheduleAtFixedRate(new TimerTask() {
// Variables as member variables instead of local variables in main()
private double lastPrice = 0;
private Price priceObject = new Price();
private double price = 0;

public void run() {
price = priceObject.getNextPrice(lastPrice);
System.out.println();
lastPrice = price;
}
}, delay, period);
}
Ответ 2

Чтобы избежать странных побочных эффектов с замыканиями в java, переменные, на которые ссылается анонимный делегат, должны быть помечены как final, поэтому для ссылки на lastPrice и price в задаче timer они должны быть помечены как final.

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

public class Foo {
private PriceObject priceObject;
private double lastPrice;
private double price;

public Foo(PriceObject priceObject) {
this.priceObject = priceObject;
}

public void tick() {
price = priceObject.getNextPrice(lastPrice);
lastPrice = price;
}
}

теперь просто создайте новый Foo как final и вызовите .отметьте таймер.

public static void main(String args[]){
int period = 2000;
int delay = 2000;

Price priceObject = new Price();
final Foo foo = new Foo(priceObject);

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
foo.tick();
}
}, delay, period);
}
Ответ 3

Вы можете получить доступ к конечным переменным только из содержащего их класса при использовании анонимного класса. Поэтому вам нужно объявить используемые переменные final (что для вас не вариант, поскольку вы меняете lastPrice и price), или не использовать анонимный класс.

Итак, ваши варианты - создать реальный внутренний класс, в который вы можете передавать переменные и использовать их обычным способом

или:

Есть быстрый (и, на мой взгляд, уродливый) способ взломать ваши переменные lastPrice и price, который заключается в объявлении их следующим образом

final double lastPrice[1];
final double price[1];

и в вашем анонимном классе вы можете установить значение следующим образом

price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];
Ответ 4

Хорошие объяснения, почему вы не можете сделать то, что пытаетесь сделать, уже предоставлены. В качестве решения, возможно, рассмотреть:

public class foo
{
static class priceInfo
{
public double lastPrice = 0;
public double price = 0;
public Price priceObject = new Price ();
}

public static void main ( String args[] )
{

int period = 2000;
int delay = 2000;

final priceInfo pi = new priceInfo ();
Timer timer = new Timer ();

timer.scheduleAtFixedRate ( new TimerTask ()
{
public void run ()
{
pi.price = pi.priceObject.getNextPrice ( pi.lastPrice );
System.out.println ();
pi.lastPrice = pi.price;

}
}, delay, period );
}
}

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

java