a
solo puede ser final aquí. ¿Por qué? ¿Cómo puedo volver a asignara
en elonClick()
método sin manteniéndola como miembro privado?private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; } }); }
¿Cómo puedo devolver el
5 * a
cuando hizo clic? Quiero decir,private void f(Button b, final int a){ b.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { int b = a*5; return b; // but return type is void } }); }
java
event-handling
anonymous-class
Andrii Abramov
fuente
fuente
Respuestas:
Como se señaló en los comentarios, algo de esto se vuelve irrelevante en Java 8, donde
final
puede estar implícito. Sin embargo, solo se puede usar una variable final efectiva en una clase interna anónima o una expresión lambda.Básicamente se debe a la forma en que Java gestiona los cierres .
Cuando crea una instancia de una clase interna anónima, todas las variables que se utilizan dentro de esa clase tienen sus valores copiados a través del constructor autogenerado. Esto evita que el compilador tenga que generar automáticamente varios tipos adicionales para mantener el estado lógico de las "variables locales", como por ejemplo el compilador de C # lo hace ... (Cuando C # captura una variable en una función anónima, realmente captura la variable: el El cierre puede actualizar la variable de una manera que es vista por el cuerpo principal del método, y viceversa).
Como el valor se ha copiado en la instancia de la clase interna anónima, sería extraño que la variable pudiera modificarse por el resto del método; podría tener un código que parece estar funcionando con una variable desactualizada ( porque eso es efectivamente lo que sucedería ... estarías trabajando con una copia tomada en un momento diferente). Del mismo modo, si pudiera realizar cambios dentro de la clase interna anónima, los desarrolladores podrían esperar que esos cambios sean visibles dentro del cuerpo del método de inclusión.
Hacer que la variable final elimine todas estas posibilidades, ya que el valor no se puede cambiar en absoluto, no necesita preocuparse por si dichos cambios serán visibles. Las únicas formas de permitir que el método y la clase interna anónima vean los cambios de cada uno es usar un tipo mutable de alguna descripción. Esta podría ser la clase que la encierra, una matriz, un tipo de envoltorio mutable ... algo así. Básicamente es un poco como comunicarse entre un método y otro: los cambios realizados en los parámetros de un método no son vistos por quien llama, pero sí los cambios realizados en los objetos a los que se refieren los parámetros.
Si está interesado en una comparación más detallada entre los cierres de Java y C #, tengo un artículo que profundiza más. Quería centrarme en el lado de Java en esta respuesta :)
fuente
final
(pero aún debe ser efectivamente final).Hay un truco que permite a la clase anónima actualizar datos en el ámbito externo.
Sin embargo, este truco no es muy bueno debido a los problemas de sincronización. Si el controlador se invoca más tarde, debe 1) sincronizar el acceso a res si el controlador se invocó desde el hilo diferente 2) debe tener algún tipo de indicador o indicación de que se actualizó res
Sin embargo, este truco funciona bien si la clase anónima se invoca en el mismo hilo inmediatamente. Me gusta:
fuente
List.forEach
código es seguro.Una clase anónima es una clase interna y la regla estricta se aplica a las clases internas (JLS 8.1.3) :
Todavía no he encontrado una razón o una explicación sobre jls o jvms, pero sí sabemos que el compilador crea un archivo de clase separado para cada clase interna y tiene que asegurarse de que los métodos declarados en este archivo de clase ( en el nivel de código de byte) al menos tener acceso a los valores de las variables locales.
( Jon tiene la respuesta completa : mantengo esta sin borrar porque uno podría estar interesado en la regla JLS)
fuente
Puede crear una variable de nivel de clase para obtener el valor devuelto. quiero decir
ahora puedes obtener el valor de K y usarlo donde quieras.
La respuesta de tu por qué es:
Una instancia de clase interna local está vinculada a la clase Main y puede acceder a las variables locales finales de su método contenedor. Cuando la instancia usa un local final de su método de contención, la variable retiene el valor que tenía en el momento de la creación de la instancia, incluso si la variable ha salido del alcance (esta es efectivamente la versión cruda y limitada de los cierres de Java).
Como una clase interna local no es miembro de una clase o paquete, no se declara con un nivel de acceso. (Sea claro, sin embargo, que sus propios miembros tienen niveles de acceso como en una clase normal).
fuente
Bueno, en Java, una variable puede ser final no solo como un parámetro, sino como un campo de nivel de clase, como
o como una variable local, como
Si desea acceder y modificar una variable desde una clase anónima, puede hacer que la variable sea una variable de nivel de clase en la clase adjunta .
No puede tener una variable como final y darle un nuevo valor.
final
significa solo eso: el valor es inmutable y final.Y como es final, Java puede copiarlo de forma segura en clases anónimas locales. No está obteniendo alguna referencia a int (especialmente porque no puede tener referencias a primitivas como int en Java, solo referencias a objetos ).
Simplemente copia el valor de a en un int implícito llamado a en su clase anónima.
fuente
static
. Quizás sea más claro si usa "variable de instancia" en su lugar.La razón por la que el acceso se ha restringido solo a las variables finales locales es que si todas las variables locales se hicieran accesibles, primero tendrían que copiarse en una sección separada donde las clases internas pueden tener acceso a ellas y mantener múltiples copias de Las variables locales mutables pueden conducir a datos inconsistentes. Mientras que las variables finales son inmutables y, por lo tanto, cualquier cantidad de copias no tendrá ningún impacto en la consistencia de los datos.
fuente
Int
se reasigna un dentro de un cierre, cambie esa variable a una instancia deIntRef
(esencialmente unInteger
contenedor mutable ). Cada acceso variable se reescribe en consecuencia.Para comprender la justificación de esta restricción, considere el siguiente programa:
El interfaceInstance permanece en la memoria después de los initialize método devuelve, pero el parámetro val no lo hace. La JVM no puede acceder a una variable local fuera de su alcance, por lo que Java hace que la llamada posterior a printInteger funcione copiando el valor de val en un campo implícito del mismo nombre dentro de interfaceInstance . Se dice que interfaceInstance ha capturado el valor del parámetro local. Si el parámetro no fuera definitivo (o efectivamente final), su valor podría cambiar, quedando fuera de sincronía con el valor capturado, lo que podría causar un comportamiento poco intuitivo.
fuente
Los métodos dentro de una clase interna anónima pueden invocarse mucho después de que el hilo que engendró haya terminado. En su ejemplo, la clase interna se invocará en el hilo de envío de eventos y no en el mismo hilo que el que la creó. Por lo tanto, el alcance de las variables será diferente. Por lo tanto, para proteger estos problemas de alcance de asignación variable, debe declararlos definitivos.
fuente
Cuando se define una clase interna anónima dentro del cuerpo de un método, todas las variables declaradas finales en el alcance de ese método son accesibles desde la clase interna. Para valores escalares, una vez que se ha asignado, el valor de la variable final no puede cambiar. Para valores de objeto, la referencia no puede cambiar. Esto permite que el compilador de Java "capture" el valor de la variable en tiempo de ejecución y almacene una copia como un campo en la clase interna. Una vez que el método externo ha finalizado y su marco de pila se ha eliminado, la variable original desaparece pero la copia privada de la clase interna persiste en la memoria de la clase.
( http://en.wikipedia.org/wiki/Final_%28Java%29 )
fuente
fuente
Como Jon responde a los detalles de implementación, otra posible respuesta sería que la JVM no quiere manejar el registro escrito que ha finalizado su activación.
Considere el caso de uso donde sus lambdas en lugar de aplicarse, se almacenan en algún lugar y se ejecutan más tarde.
Recuerdo que en Smalltalk obtendrías una tienda ilegal cuando hagas tal modificación.
fuente
Prueba este código,
Cree la Lista de matrices y ponga valor dentro de eso y devuélvalo
fuente
La clase anónima de Java es muy similar al cierre de Javascript, pero Java lo implementa de manera diferente. (verifique la respuesta de Andersen)
Entonces, para no confundir al Desarrollador Java con el comportamiento extraño que podría ocurrir para aquellos que provienen de un fondo Javascript. Supongo que es por eso que nos obligan a usar
final
, esta no es la limitación de JVM.Veamos el ejemplo de Javascript a continuación:
En Javascript, el
counter
valor será 100, porque solo hay unocounter
variable de principio a fin.Pero en Java, si no hay
final
, se imprimirá0
, porque mientras se crea el objeto interno, el0
valor se copia en las propiedades ocultas del objeto de clase interna. (aquí hay dos variables enteras, una en el método local y otra en las propiedades ocultas de la clase interna)Entonces, cualquier cambio después de la creación del objeto interno (como la línea 1), no afectará al objeto interno. Por lo tanto, creará confusión entre dos resultados y comportamientos diferentes (entre Java y Javascript).
Creo que es por eso que Java decide obligarlo a ser definitivo, por lo que los datos son 'consistentes' desde el principio hasta el final.
fuente
Variable final de Java dentro de una clase interna
la clase interna solo puede usar
Object
...)int
...) tipo puede ser envuelto por un tipo de referencia final.IntelliJ IDEA
puede ayudarlo a convertirlo en una matriz de elementosCuando el compilador genera un
non static nested
(inner class
) [Acerca de] - una nueva clase -<OuterClass>$<InnerClass>.class
se crea y los parámetros enlazados se pasan al constructor [Variable local en la pila] . Es similar al cierreLa variable final es una variable que no se puede reasignar. La variable de referencia final todavía se puede cambiar modificando un estado
Era posible que fuera extraño porque como programador podrías hacer así
fuente
Quizás este truco te dé una idea
fuente