java - reales - ¿Por qué una variable "local" no final no se puede usar dentro de una clase interna, y en su lugar puede ser un campo no final de la clase adjunta?
libro de android studio en español pdf (4)
El valor que use debe ser final, pero los campos no finales de una referencia final pueden cambiarse. Nota: this
es implícitamente una referencia final. No puedes cambiarlo
private String enclosingClassField;
private void updateStatus() {
final MutableClass ms = new MutableClass(1, 2);
Runnable doUpdateStatus = new Runnable() {
public void run() {
// you can use `EnclosingClass.this` because its is always final
EnclosingClass.this.enclosingClassField = "";
// shorthand for the previous line.
enclosingClassField = "";
// you cannot change `ms`, but you can change its mutable fields.
ms.x = 3;
}
}
/* do something with doUpdateStatus, like SwingUtilities.invokeLater() */
}
Hay algunos temas sobre Desbordamiento de pila en el error del compilador Cannot refer to a non-final variable message inside an inner class defined in a different method
y la solución es "declararlo como final y listo", pero con esta pregunta teórica Me gustaría inspeccionar cuál es la razón lógica por la cual este código no puede compilarse:
private void updateStatus(String message) {
Runnable doUpdateStatus = new Runnable() {
public void run() {
/* do something with message */
}
}
/* do something with doUpdateStatus, like SwingUtilities.invokeLater() */
}
(solución: declarar message
como final) mientras que este hace:
private String enclosingClassField;
private void updateStatus() {
Runnable doUpdateStatus = new Runnable() {
public void run() {
/* do something with enclosingClassField */
}
}
/* do something with doUpdateStatus, like SwingUtilities.invokeLater() */
}
Estoy realmente confundido. enclosingClassField
no es definitivo, puede cambiar cada vez muchas veces, mientras que el argumento de message
deficiente de updateStatus
solo puede cambiar dentro de su cuerpo de método, y en su lugar es culpado por el compilador;)
Incluso el error del compilador es engañoso para mí. Cannot refer to a non-final variable message inside an inner class defined in a different method
: ¿ diferente de qué? ¿El message
no está definido en el mismo método que la clase interna? ¿En su lugar, no se enclosingClassField
ClassField fuera del método? Uhm ...
¿Puede alguien señalarme la interpretación correcta de este asunto? Gracias.
La diferencia es entre las variables locales (método) y las variables miembro de la clase. Una variable miembro existe durante la vigencia del objeto adjunto, por lo que la instancia de la clase interna puede hacer referencia a ella. Sin embargo, una variable local solo existe durante la invocación del método, y el compilador la maneja de manera diferente, ya que se genera una copia implícita de ella como el miembro de la clase interna. Sin declarar la variable local final, se podría cambiar, lo que llevaría a errores sutiles debido a que la clase interna todavía hace referencia al valor original de esa variable.
Actualización: El Boletín de Especialistas de Java # 25 discute esto en más detalle.
Incluso el error del compilador es engañoso para mí.
Cannot refer to a non-final variable message inside an inner class defined in a different method
: ¿ diferente de qué?
Desde el método de run
la clase interna, creo.
La razón es que Java no admite closures . No hay comandos de JVM para acceder a la variable local desde fuera del método, mientras que se puede acceder fácilmente a los campos de clase desde cualquier lugar.
Entonces, cuando usas la variable local final
en una clase interna, el compilador realmente pasa un valor de esa variable al constructor de la clase interna. Obviamente, no funcionará para variables no final
, ya que el valor puede cambiar después de la construcción de la clase interna.
Los campos de clase contenedora no tienen este problema porque el compilador pasa implícitamente una referencia a la clase contenedora en el constructor de la clase interna, por lo que puede acceder a sus campos de forma normal, al acceder a los campos de cualquier otra clase.
tres tipos de cosas: variables de instancia, variables locales y objetos:
■ Instance variables and objects live on the heap. ■ Local variables live on the stack.
El objeto de la clase interna no puede usar las variables locales del método en el que se define la clase interna local.
porque el uso de variables locales del método es que las variables locales del método se mantienen en la pila y se pierden tan pronto como el método finaliza.
Pero incluso después de que el método finalice, el objeto de la clase interna local aún puede estar vivo en el montón. Método La clase interna local aún puede usar las variables locales que están marcadas como finales.
La variable final JVM toma esto como una constante, ya que no cambiará después de iniciado. Y cuando una clase interna intenta acceder a ellos, crea una copia de esa variable en el montón y crea un campo sintético dentro de la clase interna, por lo que incluso cuando la ejecución del método haya terminado, es accesible porque la clase interna tiene su propia copia .
campo sintético, que en realidad no existe en el código fuente, pero el compilador crea esos campos en algunas clases internas para hacer que esos campos sean accesibles.