Until Java 7, using external local scope variables within anonymous classes would not work if they were not final
:
public void metodoQualquer() {
int a = 5;
int b = 7;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(a + b); // Erro de compilação aqui!
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
// Ignora a exceção.
}
}
The reason is that local variables are allocated in the stack frame of the local method call, and nothing guarantees that when the anonymous class method is executed, that stack frame will still exist. However, if the variable is declared final
, then the compiler can synthesize a constructor of the anonymous class responsible for copying these values, and since they are final
, then the value used inside the anonymous class will always reflect the value of the stack frame . So the above code could be rewritten like this:
public void metodoQualquer() {
final int a = 5;
final int b = 7;
Thread t = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(a + b); // Agora não dá erro de compilação aqui.
}
});
t.start();
try {
t.join();
} catch (InterruptedException e) {
// Ignora a exceção.
}
}
Java 8 inherits this same rule in the lambdas implementation. However, since being forced to always declare final
is very annoying, the concept of effectively final has emerged, which basically means that if the variable does not change its value, even if the modifier final
is not present, so for all practical purposes it is as if it actually had the final
modifier. So in Java 8 I can write the above code this way:
public void metodoQualquer() {
int a = 5;
int b = 7;
// Não dá erro de compilação aqui, mesmo que o a e o b não tenham sido declarados como final.
Thread t = new Thread(() -> System.out.println(a + b));
t.start();
try {
t.join();
} catch (InterruptedException e) {
// Ignora a exceção.
}
}
However, if the variable can be modified (that is, it can not be implicitly considered as final
), then the schema no longer works:
public void metodoQualquer() {
int a = 5;
int b = 7;
// Erro de compilação aqui, a não é final ou effectively final.
Thread t = new Thread(() -> a = a + b);
t.start();
try {
t.join();
} catch (InterruptedException e) {
// Ignora a exceção.
}
}
In your case, at least one of the variables fichaAtend
or jTxtLog
is not effectively , because at least one of them is a local variable that does not have the final
modifier and that it also undergoes modifications after having first assigned its value.
There are four solutions to this. Choose the one that is best for you:
The first solution is to change the structure of the external scope to ensure that these variables are final
or effectively .
The second solution is to declare another variable effectively and assign the value of these variables:
TipoDoFichaAtend fichaAtend2 = fichaAtend;
TipoDoJTxtLog jTxtLog2 = jTxtLog;
listaAtendimento.stream().forEach(atendimentoFicha ->
{
fichaAtend2.getAtendimentosIndividuais().add(getAtendIndivChild(jTxtLog2, atendimentoFicha));
});
There are cases where the second solution is not adwanted, because it only works if the value of the variable is not further modified in the external scope or if these modifications are not to be reflected in the internal scope. It also does not work if the internal scope modifies the variables of the external scope. In this case, we have a third solution, which is to encapsulate the value in a mutable object stored in an effectively final variable. The type AtomicReference
is a good candidate for this:
AtomicReference<TipoDoFichaAtend> fichaAtend2 = new AtomicReference<>(fichaAtend);
AtomicReference<TipoDoJTxtLog> jTxtLog2 = new AtomicReference<>(jTxtLog);
listaAtendimento.stream().forEach(atendimentoFicha ->
{
fichaAtend2.get().getAtendimentosIndividuais().add(getAtendIndivChild(jTxtLog2.get(), atendimentoFicha));
jTxtLog2.set(oQueVoceQuiser); // Exemplo de modificação do conteúdo da variável.
});
TipoDoJTxtLog resultado = jTxtLog2.get(); // Trará o resultado após modificações no escopo interno.
Another alternative to the simplest%, but more gambiarrosa and non-thread-safe , is to use an array of a single position to encapsulate the object :
TipoDoFichaAtend[] fichaAtend2 = {fichaAtend};
TipoDoJTxtLog[] jTxtLog2 = {jTxtLog};
listaAtendimento.stream().forEach(atendimentoFicha ->
{
fichaAtend2[0].getAtendimentosIndividuais().add(getAtendIndivChild(jTxtLog2[0], atendimentoFicha));
jTxtLog2[0] = oQueVoceQuiser; // Exemplo de modificação do conteúdo da variável.
});
TipoDoJTxtLog resultado = jTxtLog2[0]; // Trará o resultado após modificações no escopo interno.
The last solution is to restructure your code so that it no longer needs to use lambda . This situation is limited and can not always be applied, but it is often the case in lambda that can be replaced by for or enhanced-for .
Finally, in the last example you gave, the only variable of the external scope that is used in the inner scope is the variable AtomicReference
, which is effectively because its value is only assigned only once. And because of that, the code in this example compiles.