Editado: necesito cambiar los valores de varias variables, ya que se ejecutan varias veces a través de un temporizador. Necesito seguir actualizando los valores con cada iteración a través del temporizador. No puedo establecer los valores como finales ya que eso me impedirá actualizar los valores, sin embargo, recibo el error que describo en la pregunta inicial a continuación:
Anteriormente había escrito lo que está debajo:
Recibo el error "no puede referirse a una variable no final dentro de una clase interna definida en un método diferente".
Esto está sucediendo para el precio llamado doble y el precio llamado precioObjeto. ¿Sabes por qué me sale este problema? No entiendo por qué necesito tener una declaración final. Además, si puede ver qué es lo que estoy tratando de hacer, qué debo 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);
}
fuente
Respuestas:
Java no admite cierres verdaderos , aunque usar una clase anónima como la que está 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 hastamain()
que regrese el método.Cuando el
main()
método regrese, las variables locales (comolastPrice
yprice
) se limpiarán de la pila, por lo que ya no existirán después de losmain()
retornos.Pero el objeto de clase anónimo hace referencia a estas variables. Las cosas saldrían terriblemente mal si el objeto de clase anónimo intenta acceder a las variables después de que se hayan limpiado.
Al hacer
lastPrice
yprice
final
, ya no son realmente variables, sino constantes. El compilador simplemente puede reemplazar el uso delastPrice
yprice
en la clase anónima con los valores de las constantes (en tiempo de 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 especialmente esas variables, 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: Puedes hacer esto:
fuente
Para evitar efectos secundarios extraños con los cierres en las variables de Java a las que hace referencia un delegado anónimo, debe marcarse como final, por lo tanto, para referirse a ellos
lastPrice
y al precio dentro de la tarea del temporizador, deben marcarse como finales.Obviamente, esto no funcionará para usted porque desea cambiarlos, en este caso debe considerar encapsularlos dentro de una clase.
ahora solo crea un nuevo Foo como final y llama .tick desde el temporizador.
fuente
Solo puede acceder a las variables finales de la clase que lo contiene cuando usa una clase anónima. Por lo tanto, debe declarar las variables que se usan como finales (lo cual no es una opción para usted ya que está cambiando lastPrice and price ), o no use una clase anónima.
Por lo tanto, 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 variable de precio que es declararlo así
y en tu clase anónima puedes establecer el valor así
fuente
Buenas explicaciones de por qué no puede hacer lo que está tratando de hacer ya proporcionado. Como solución, tal vez considere:
Parece que probablemente podría hacer un mejor diseño que eso, pero la idea es que podría agrupar las variables actualizadas dentro de una referencia de clase que no cambia.
fuente
Con las clases anónimas, en realidad estás declarando una clase anidada "sin nombre". Para las clases anidadas, el compilador genera una nueva clase pública independiente con un constructor que tomará todas las variables que usa como argumentos (para las clases anidadas "con nombre", esta siempre es una instancia de la clase original / adjunta). Esto se hace porque el entorno de tiempo de ejecución no tiene noción de clases anidadas, por lo que debe haber una conversión (automática) de una clase anidada a una independiente.
Tome este código por ejemplo:
Eso no funcionará, porque esto es lo que hace el compilador debajo del capó:
La clase anónima original se reemplaza por una clase independiente que genera el compilador (el código no es exacto, pero debería darle una buena idea):
Como puede ver, la clase independiente tiene una referencia al objeto compartido, recuerde que todo en Java es paso por valor, por lo que incluso si la variable de referencia 'compartida' en EnclosingClass cambia, la instancia a la que apunta no se modifica , y todas las demás variables de referencia que lo apuntan (como la de la clase anónima: Incluyendo $ 1), no se darán cuenta de esto. Esta es la razón principal por la que el compilador lo obliga a declarar estas variables 'compartidas' como finales, para que este tipo de comportamiento no se convierta en su código ya en ejecución.
Ahora, esto es lo que sucede cuando usa una variable de instancia dentro de una clase anónima (esto es lo que debe hacer para resolver su problema, mover su lógica a un método de "instancia" o un constructor de una clase):
Esto compila bien, porque el compilador modificará el código, de modo que la nueva clase generada Enclosing $ 1 tendrá una referencia a la instancia de EnclosingClass donde se instancia (esto es solo una representación, pero debería ayudarlo):
De esta manera, cuando la variable de referencia 'compartida' en EnclosingClass se reasigna, y esto sucede antes de la llamada a Thread # run (), verá "otro hola" impreso dos veces, porque ahora la variable de encierro EnclosingClass $ 1 # mantendrá una referencia al objeto de la clase donde se declaró, por lo que los cambios en cualquier atributo de ese objeto serán visibles para las instancias de EnclosingClass $ 1.
Para obtener más información sobre el tema, puede ver esta excelente publicación de blog (no escrita por mí): http://kevinboone.net/java_inner.html
fuente
Cuando me encuentro con este problema, simplemente paso los objetos a la clase interna a través del constructor. Si necesito pasar primitivas u objetos inmutables (como en este caso), se necesita una clase contenedora.
Editar: En realidad, no uso una clase anónima en absoluto, sino una subclase adecuada:
fuente
No puede hacer referencia a variables no finales porque la especificación del lenguaje Java lo dice. De 8.1.3:
"Cualquier variable local, parámetro de método formal o parámetro de controlador de excepción utilizado pero no declarado en una clase interna debe declararse final". Párrafo entero.
Solo puedo ver una parte de su código; según yo, la modificación de la programación de variables locales es una idea extraña. Las variables locales dejan de existir cuando abandonas la función. ¿Quizás los campos estáticos de una clase serían mejores?
fuente
Acabo de escribir algo para manejar algo según la intención de los autores . Encontré que lo mejor que podía hacer era dejar que el constructor tomara todos los objetos y luego, en su método implementado, usara esos objetos constructores.
Sin embargo, si está escribiendo una clase de interfaz genérica, debe pasar un Objeto, o mejor, una lista de Objetos. Esto podría ser hecho por Object [] o incluso mejor, Object ... porque es más fácil de llamar.
Vea mi pieza de ejemplo justo debajo.
Consulte esta publicación sobre cierres de Java que admite esto de forma inmediata: http://mseifed.blogspot.se/2012/09/closure-implementation-for-java-5-6-and.html
La versión 1 admite la aprobación de cierres no finales con transmisión automática:
https://github.com/MSeifeddo/Closure-implementation-for-Java-5-6-and-7/blob/master/org/mo/closure/v1/ Closure.java
fuente
Si desea cambiar un valor en una llamada de método dentro de una clase anónima, ese "valor" es en realidad un
Future
. Entonces, si usas guayaba, puedes escribirfuente
Una solución que he notado no se menciona (a menos que me la haya perdido, si lo hice, corríjame), es el uso de una variable de clase. Encontré este problema al intentar ejecutar un nuevo hilo dentro de un método:
new Thread(){ Do Something }
.Llamar
doSomething()
desde lo siguiente funcionará. No necesariamente tiene que declararlofinal
, solo necesita cambiar el alcance de la variable para que no se recopile antes de la clase interna. Esto es a menos, por supuesto, que su proceso sea enorme y que cambiar el alcance pueda crear algún tipo de conflicto. No quería hacer mi variable final ya que de ninguna manera era una final / constante.fuente
Si la variable requiere ser final, no puede serlo, entonces puede asignar el valor de la variable a otra variable y hacer ESO final para que pueda usarla en su lugar.
fuente
use ClassName.this.variableName para hacer referencia a la variable no final
fuente
simplemente puede declarar la variable fuera de la clase externa. Después de esto, podrá editar la variable desde dentro de la clase interna. A veces me enfrento a problemas similares al codificar en Android, por lo que declaro la variable como global y funciona para mí.
fuente
¿Puedes hacer
lastPrice
,priceObject
yprice
campos de la clase interna anónima?fuente
La principal preocupación es si una variable dentro de la instancia de clase anónima se puede resolver en tiempo de ejecución. No es imprescindible hacer que una variable sea final siempre que se garantice que la variable está dentro del alcance del tiempo de ejecución. Por ejemplo, vea las dos variables _statusMessage y _statusTextView dentro del método updateStatus ().
fuente
lo que funcionó para mí es solo definir la variable fuera de esta función tuya.
Justo antes de declarar la función principal, es decir
fuente
Declare la variable como estática y haga referencia en el método requerido usando className.variable
fuente
Non-static parameter cannot be referenced from a static context
Solo otra explicación. Considere este ejemplo a continuación
Aquí la salida será
m1 completa
Hilo t corriendo
Hilo t corriendo
Hilo t corriendo
................
Ahora el método m1 () se completa y asignamos la variable de referencia o a nulo, Now Outer Class Object es elegible para GC pero Inner Class Object todavía existe quien tiene una relación (Has-A) con el objeto Thread que se está ejecutando. Sin el objeto de clase externa existente no hay posibilidad de que exista el método m1 () y sin el método de m1 () existente no hay posibilidad de que exista su variable local, pero si el objeto de clase interna usa la variable local del método m1 () entonces todo se explica por sí mismo .
Para resolver esto, tenemos que crear una copia de la variable local y luego copiar en el montón con el objeto de clase Inner, lo que Java hace solo para la variable final porque en realidad no son variables, son como constantes (todo sucede solo en tiempo de compilación no en tiempo de ejecución).
fuente
Para resolver el problema anterior, diferentes idiomas toman decisiones diferentes.
para Java, la solución es la que vemos en este artículo.
para C #, la solución es permitir efectos secundarios y la captura por referencia es la única opción.
para C ++ 11, la solución es permitir que el programador tome la decisión. Pueden elegir capturar por valor o por referencia. Si se captura por valor, no se producirán efectos secundarios porque la variable referenciada es realmente diferente. Si se captura por referencia, pueden ocurrir efectos secundarios, pero el programador debe darse cuenta.
fuente
Porque es confuso si la variable no es final, ya que los cambios no se recogerán en la clase anónima.
Simplemente haga que las variables 'precio' y 'lastPrice' sean finales.
- Editar
Vaya, y también necesitarás no asignarles, obviamente, en tu función. Necesitarás nuevas variables locales. De todos modos, sospecho que alguien ya te ha dado una mejor respuesta.
fuente