Estoy jugando con lambdas en Java 8 y me encontré con una advertencia local variables referenced from a lambda expression must be final or effectively final
. Sé que cuando uso variables dentro de una clase anónima deben ser finales en la clase externa, pero aún así, ¿cuál es la diferencia entre final y efectivamente final ?
351
Respuestas:
Por ejemplo, suponga que la variable
numberLength
no se declara final y agrega la instrucción de asignación marcada en elPhoneNumber
constructor:Debido a esta declaración de asignación, la variable numberLength ya no es efectivamente final. Como resultado, el compilador de Java genera un mensaje de error similar a "las variables locales a las que se hace referencia desde una clase interna deben ser finales o efectivamente finales" donde la clase interna PhoneNumber intenta acceder a la variable numberLength:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
fuente
numberLength
convierta en una variable local de este método.Creo que la forma más simple de explicar "efectivamente final" es imaginar agregar el
final
modificador a una declaración de variable. Si, con este cambio, el programa continúa comportándose de la misma manera, tanto en tiempo de compilación como en tiempo de ejecución, entonces esa variable es efectivamente final.fuente
case k
requiere una expresión constante que podría ser una variable constante ("Una variable constante es una variable final de tipo primitivo o tipo String que se inicializa con una expresión constante" JLS 4.12.4 ), que es un caso especial de una variable final variable.Según los documentos :
Básicamente, si el compilador encuentra que una variable no aparece en asignaciones fuera de su inicialización, entonces la variable se considera efectivamente final .
Por ejemplo, considere alguna clase:
fuente
bar
en su ejemplo no es una variable local, sino un campo. "Efectivamente final" en el mensaje de error anterior no se aplica a los campos en absoluto.bar
es un parámetro aquí, no un campo.'Efectivamente final' es una variable que no daría un error del compilador si fuera agregada por 'final'
De un artículo de 'Brian Goetz',
lambda-state-final- Brian Goetz
fuente
Esta variable a continuación es final , por lo que no podemos cambiar su valor una vez inicializada. Si lo intentamos, obtendremos un error de compilación ...
Pero si creamos una variable como esta, podemos cambiar su valor ...
Pero en Java 8 , todas las variables son finales por defecto. Pero la existencia de la segunda línea en el código lo hace no final . Entonces, si eliminamos la segunda línea del código anterior, nuestra variable ahora es "efectivamente final" ...
Entonces .. Cualquier variable que se asigne una vez y solo una vez, es "efectivamente final" .
fuente
Una variable es final o efectivamente final cuando se inicializa una vez y nunca muta en su clase de propietario. Y no podemos inicializarlo en bucles o clases internas .
Final :
Efectivamente final :
fuente
Cuando una expresión lambda usa una variable local asignada de su espacio cerrado, existe una restricción importante. Una expresión lambda solo puede usar una variable local cuyo valor no cambie. Esa restricción se conoce como " captura variable " que se describe como; valores de captura de expresiones lambda, no variables .
Las variables locales que puede usar una expresión lambda se conocen como " efectivamente final ".
Una variable final efectiva es aquella cuyo valor no cambia después de ser asignada por primera vez. No es necesario declarar explícitamente una variable como final, aunque hacerlo no sería un error.
Veamos con un ejemplo, tenemos una variable local i que se inicializa con el valor 7, con la expresión lambda estamos tratando de cambiar ese valor asignando un nuevo valor a i. Esto generará un error del compilador: " La variable local que definí en un ámbito de inclusión debe ser final o efectivamente final "
fuente
El tema final efectivo se describe en JLS 4.12.4 y el último párrafo consiste en una explicación clara:
fuente
final es una variable declarar con palabra clave
final
, ejemplo:permanece
final
durante todo el programa.efectivamente final : cualquier variable o parámetro local al que se le asigna un valor solo una vez en este momento (o se actualiza solo una vez). Puede que no permanezca efectivamente final durante todo el programa. Esto significa que la variable final efectiva podría perder su propiedad final efectiva inmediatamente después del tiempo que se le asigna / actualiza al menos una asignación más. ejemplo:
fuente
final
palabra clave a una declaración sin introducir errores de compilación, entonces no es efectivamente final . Es el contrapositivo de esta declaración: "Si una variable es efectivamente final, agregar el modificador final a su declaración no introducirá ningún error en tiempo de compilación".Como han dicho otros, una variable o parámetro cuyo valor nunca cambia después de que se inicializa es efectivamente final. En el código anterior, si cambia el valor de
x
en la clase interna,FirstLevel
el compilador le dará el mensaje de error:fuente
Las expresiones lambda pueden acceder
variables estáticas
variables de instancia,
efectivamente los parámetros finales del método, y
efectivamente variables locales finales.
Fuente: OCP: Guía de estudio de Oracle Certified Professional Java SE 8 Programmer II, Jeanne Boyarsky, Scott Selikoff
Adicionalmente,
Fuente: Comenzando con Java: desde estructuras de control a través de objetos (6a edición), Tony Gaddis
Además, no olvide el significado de
final
que se inicializa exactamente una vez antes de que se use por primera vez.fuente
Declarar una variable
final
o no declararlafinal
, pero mantenerla efectivamente final puede resultar (depende del compilador) en un código de bytes diferente.Echemos un vistazo a un pequeño ejemplo:
El
main
código de bytes correspondiente del método (Java 8u161 en Windows 64 Bit):La tabla de números de línea correspondiente:
Como vemos el código fuente en líneas
12
,13
,14
no aparece en el código de bytes. Eso es porquei
estrue
y no cambiará su estado. Por lo tanto, este código es inalcanzable (más en esta respuesta ). Por la misma razón, el código en la línea9
también falla. El estado dei
no tiene que ser evaluado, ya que estrue
seguro.Por otro lado, aunque la variable
j
es efectivamente final , no se procesa de la misma manera. No hay tales optimizaciones aplicadas. El estado dej
se evalúa dos veces. El código de bytes es el mismo independientemente de quej
sea efectivamente final .fuente
La variable final efectiva es una variable local que es:
final
Mientras que una variable final es una variable que es:
final
palabra clave.fuente
Esto no comenzó en Java 8, lo uso desde hace mucho tiempo. Este código solía (antes de Java 8) ser legal:
fuente
final
se puede acceder a las variables, pero en Java 8 también a aquellas que son efectivamente finales.