2010-10-11 12 views
42
final JTextField jtfContent = new JTextField(); 
btnOK.addActionListener(new java.awt.event.ActionListener(){ 
    public void actionPerformed(java.awt.event.ActionEvent event){ 
     jtfContent.setText("I am OK"); 
    } 
}); 

Si Omito final, veo el error "no puede referirse a un jtfContent variable no definitiva dentro de una clase interna definida en un método diferente".¿Por qué las clases internas de Java requieren variables de instancia externas "finales"?

¿Por qué una clase interna anónima necesita que la variable de instancia de las clases externas sea definitiva para poder acceder a ella?

+1

Observo que esta pregunta es anterior a la pregunta de la que se dice que es un duplicado. – Raedwald

+2

@Raedwald según la meta-discusión respectiva, el tiempo de las preguntas realmente no importa: [Debería votar para cerrar una pregunta duplicada, a pesar de que es mucho más nueva ...] (http://meta.stackexchange.com/q/ 147643/165773) - _ "Si la nueva pregunta es una mejor pregunta o tiene mejores respuestas, vote para cerrar la anterior como un duplicado de la nueva ..." _ – gnat

+0

alguien puede explicar por qué en Java 1.8 este código pasa ¿Compilacion? – Strin

Respuesta

60

Bueno en primer lugar, vamos todos a relajarse, y por favor Baja el arma.

OK. Ahora, la razón por la que el lenguaje insiste en eso es que hace trampa para proporcionar acceso a las funciones de clase interna a las variables locales que anhelan. El tiempo de ejecución hace una copia del contexto de ejecución local (y etc., según corresponda) y, por lo tanto, insiste en que haga todo final para que todo sea honesto.

Si no lo hizo, entonces el código que cambia el valor de una variable local después de que su objeto se construyó pero antes se ejecuta la función de la clase interna puede ser confuso y extraño.

Esta es la esencia de mucho alboroto en torno a Java y "cierres".


Nota: el párrafo inicial era una broma en referencia a un texto en mayúsculas en la composición original de la OP.

+0

Tuve esto una vez con Javascript. ¡Fue difícil de encontrar! –

+0

¿es una copia superficial del tiempo de ejecución del contexto de ejecución local o una copia profunda? Traté de probar esto y parece que es una copia superficial, ¿puedes confirmarlo? – j2emanue

+2

@ j2emanue esa es una buena pregunta; I * fuertemente * sospecho que es una copia poco profunda (por lo tanto, si tiene una referencia 'final' a un objeto mutable, el objeto será mutable a través del código en el pseudo-cierre). Hacer copias profundas generalmente es un problema difícil y creo que sería extremadamente improbable que funcione de esa manera. – Pointy

20

Los métodos de una clase anónima en realidad no tienen acceso a variables locales y los parámetros del método. Más bien, cuando se crea una instancia un objeto de la clase anónima , copias de los finales variables locales y Parámetros de métodos contemplado por los métodos del objeto se almacenan como variables de instancia en el objeto. Los métodos en el objeto de la clase anónima realmente acceden a esas variables de instancia ocultas. [1]

Por lo tanto, las variables locales y los parámetros de método accedidos por los métodos de la clase local deben declararse finales para evitar que sus valores cambien después de la instancia del objeto.

[1] http://www.developer.com/java/other/article.php/3300881/The-Essence-of-OOP-using-Java-Anonymous-Classes.htm

+1

+1 para indicar explícitamente que la limitación (hasta java 8) era w.r.t variables locales y parámetros de método y no variables de instancia. – sactiw

5

Las variables en torno a la definición de la clase viven en la pila, por lo que probablemente desaparezcan cuando se ejecute el código dentro de la clase interna (si desea saber por qué, buscar pila y montón). Es por eso que las clases internas en realidad no usan las variables en el método contenedor, pero están construidas con copias de ellas.

Esto significa que si cambia la variable en el método que lo contiene después de construir la clase interna, su valor no cambiará en la clase interna, aunque usted lo esperaría. Para evitar confusiones allí, Java requiere que sean definitivas, por lo que espera no poder modificarlas.

6

La razón es que Java no es totalmente compatible con los llamados "cierres", en cuyo caso el final no sería necesario, pero en su lugar han encontrado un truco al permitir que el compilador genere algunas variables ocultas que se utilizan para dar funcionalidad que ves.

Si desensambla el código de bytes generado, puede ver cómo lo hace el compilador, incluidas las variables ocultas con nombres extraños que contienen copias de las variables finales.

Es una solución elegante para ofrecer funcionalidad sin tener que inclinar el idioma hacia atrás para hacerlo.


Editar: Para Java 8 lambdas dar una forma más concisa para hacer lo que se hizo anteriormente con las clases anónimas. Las restricciones en las variables también se han liberado de "final" a "esencialmente final"; no es necesario que lo declare final, pero si es tratado como si fuera final (podría agregar la palabra clave final y su código aún así compilaría) puede ser usado. Este es un cambio realmente agradable.

+1

en lugar de "elegante", podría haber dicho "poco elegante", pero es lo que es. Supongo que es un poco mejor en java8 ... :) – rogerdpack

+0

Elegante sintaxis. Y, como siempre, siéntase libre de escribir una mejor respuesta. –

Cuestiones relacionadas