No se puede hacer referencia a una variable no final dentro de una clase interna definida en un método diferente
Editado: necesito cambiar los valores de varias variables a medida que se ejecutan varias veces a través de un temporizador. Necesito seguir actualizando los valores con cada iteración del temporizador. No puedo establecer los valores en final ya que eso me impedirá actualizarlos; sin embargo, recibo el error que describo en la pregunta inicial a continuación:
Anteriormente había escrito lo que está a continuación:
Recibo el error "no se puede hacer referencia a una variable no final dentro de una clase interna definida en un método diferente".
Esto sucede para el precio doble llamado y el Precio llamado precioObjeto. ¿Sabes por qué tengo este problema? No entiendo por qué necesito tener una declaración final. Además, si puede ver qué es lo que estoy intentando hacer, qué tengo que hacer para solucionar este problema.
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);
}
Java no admite cierres verdaderos , aunque usar una clase anónima como la que estás usando aquí ( new TimerTask() { ... }
) parece una especie de cierre.
editar - Vea los comentarios a continuación - la siguiente no es una explicación correcta, como señala KeeperOfTheSoul.
Por eso no funciona:
Las variables lastPrice
y el precio son variables locales en el método main(). El objeto que cree con la clase anónima puede durar hasta que main()
regrese el método.
Cuando el main()
método regresa, las variables locales (como lastPrice
y price
) se limpiarán de la pila, por lo que ya no existirán después de main()
los retornos.
Pero el objeto de clase anónimo hace referencia a estas variables. Las cosas irían terriblemente mal si el objeto de clase anónimo intenta acceder a las variables después de haberlas limpiado.
Al hacer lastPrice
y price
final
, ya no son realmente variables, sino constantes. Luego, el compilador puede simplemente reemplazar el uso de lastPrice
y price
en la clase anónima con los valores de las constantes (en el momento de la compilación, por supuesto), y ya no tendrá el problema de acceder a variables inexistentes.
Otros lenguajes de programación que admiten cierres lo hacen tratando esas variables de manera especial, asegurándose de que no se destruyan cuando finalice el método, de modo que el cierre aún pueda acceder a las variables.
@Ankur: Podrías hacer esto:
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);
}
Para evitar efectos secundarios extraños con los cierres en las variables de Java a las que hace referencia un delegado anónimo se deben marcar como finales, por lo que para hacer referencia lastPrice
y fijar el precio dentro de la tarea del temporizador, se deben marcar como finales.
Obviamente, esto no funcionará para usted porque desea cambiarlos; en este caso, debería considerar encapsularlos dentro de una clase.
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;
}
}
ahora simplemente cree un nuevo Foo como final y llame a .tick desde el temporizador.
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);
}
Solo puede acceder a las variables finales de la clase contenedora cuando utiliza una clase anónima. Por lo tanto, debe declarar finales las variables que se utilizan (lo cual no es una opción para usted ya que está cambiando lastPrice y price ), o no usar una clase anónima.
Entonces, sus opciones son crear una clase interna real, en la que pueda pasar las variables y usarlas de manera normal.
o:
Hay un truco rápido (y en mi opinión feo) para su último precio y la variable de precio que consiste en declararlo así.
final double lastPrice[1];
final double price[1];
y en tu clase anónima puedes establecer el valor así
price[0] = priceObject.getNextPrice(lastPrice[0]);
System.out.println();
lastPrice[0] = price[0];