java - son - que es una interfaz interna
Las lambdas de Java tienen diferentes requisitos variables que las clases internas anónimas (2)
Esto no compilará
public class Example
{
private final int x;
private final int y = 2 * x;
public Example() {
x = 10;
}
}
pero esto:
public class Example
{
private final int x;
private final int y;
public Example() {
x = 10;
y = 2 * x;
}
}
y también esto:
public class Example
{
private final int x = 10;
private final int y = 2 * x;
}
Entonces no tiene nada que ver con lambdas. Un campo que se inicializa en la misma línea en la que se declara se evalúa antes de que se ejecute el constructor. Entonces, en ese punto, la variable ''val'' (o en este ejemplo ''x'') no se ha inicializado.
Tengo una clase interna anónima y una lambda equivalente. ¿Por qué las reglas de inicialización de variables son más estrictas para el lambda, y hay una solución más limpia que una clase interna anónima o inicializándola en el constructor?
import java.util.concurrent.Callable;
public class Immutable {
private final int val;
public Immutable(int val) { this.val = val; }
// Works fine
private final Callable<String> anonInnerGetValString = new Callable<String>() {
@Override
public String call() throws Exception {
return String.valueOf(val);
}
};
// Doesn''t compile; "Variable ''val'' might not have been initialized"
private final Callable<String> lambdaGetValString = () -> String.valueOf(val);
}
Editar: me encontré con una solución: usar un getter para val
.
El capítulo sobre estados de cuerpos de expresión lambda
A diferencia del código que aparece en las declaraciones de clase anónimas, el significado de los nombres y las palabras clave
this
ysuper
aparecen en un cuerpo lambda, junto con la accesibilidad de las declaraciones referenciadas, son las mismas que en el contexto circundante (excepto que los parámetros lambda introducen nombres nuevos).La transparencia de
this
(tanto explícita como implícita) en el cuerpo de una expresión lambda, es decir, tratarla igual que en el contexto circundante, permite una mayor flexibilidad para las implementaciones y evita que el significado de los nombres no calificados en el cuerpo sea dependiente. en la resolución de sobrecarga.
Son más estrictos por eso.
El contexto circundante, en este caso, es una asignación a un campo y el problema es un acceso de un campo, val
, un campo final
blanco, en el lado derecho de la expresión.
Los estados de especificación de lenguaje Java
Cada variable local (§14.4) y cada campo
final
blanco (§4.12.4, §8.3.1.2) deben tener un valor definitivamente asignado cuando se produce cualquier acceso de su valor.Un acceso a su valor consiste en el nombre simple de la variable (o, para un campo, el nombre simple del campo calificado por
this
) que aparece en cualquier parte de una expresión, excepto como el operando de la izquierda del operador de asignación simple=
(§ 15.26.1).Para cada acceso de una variable local o campo
final
blancox
,x
debe asignarse definitivamente antes del acceso, o se produce un error en tiempo de compilación.
Luego continúa diciendo
Deje que
C
sea una clase, y seaV
un campo de miembro nostatic
final
blanco deC
, declarado enC
Entonces:
V
definitivamente no está asignado (y, además, no está definitivamente asignado) antes del inicializador de instancias más a la izquierda (§8.6) o del inicializador de variables de instancia deC
V
es [un] asignado antes de que un inicializador de instancia o un inicializador de variable de instancia deC
distinto del iff más a la izquierdaV
sea [un] asignado después del inicializador de instancia precedente o el inicializador de variable de instancia deC
Tu código básicamente se ve así
private final int val;
// leftmost instance variable initializer, val still unassigned
private final Callable<String> anonInnerGetValString = ...
// still unassigned after preceding variable initializer
private final Callable<String> lambdaGetValString = ...
Por lo tanto, el compilador determina que val
sin asignar cuando se trata de acceder dentro de la expresión de inicialización para lambdaGetValString
.
Las reglas anteriores se aplican al uso de un nombre simple, val
, no a una expresión calificada, this.val
. Puedes usar
final Callable<String> lambdaGetValString = () -> String.valueOf(this.val);