In a method I have this:
int x = 0
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
}
if (x != 0) {
doLater(() -> showErrorMessage(x)); // compile error here
}
// no more reference to 'x' here
I don't understand why it produces compilation error. The error says that x
is not final or effectively-final, so it can't be accessed from the lambda body. There is no modification to x
after the doLater
call, so the value of x
is actually already determined when doLater
is called.
I am guessing that the answer to this question is because x
is not qualified to be called an effectively-final variable. However, I want to know what the reason is.
Can't the compiler just create a temporary final variable, effectively making the code like:
if (x != 0) {
final int final_x = x;
doLater(() -> showErrorMessage(final_x));
}
and everything still work the same?
Most importantly, We can use local variable as final in an anonymous inner class, we have to declare the local variable of anonymous inner class as final.
The local variables that a lambda expression may use are referred to as “effectively final”. An effectively final variable is one whose value doesn't change after it's first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error.
A variable is considered an effective final if it is not modified after initialization in the local block. This means you can now use the local variable without the final keyword inside an anonymous class or lambda expression, provided they must be effectively final.
Local Variables When final is applied to a local variable, its value must be assigned exactly once. We can assign the value in the final variable declaration or in the class constructor. In case we try to change the final variable value later, the compiler will throw an error.
Effectively final means that it could have been made final
i.e. it never changes. It means that effectively, the variable could be a final
one.
The problem is that it doesn't keep track of the last time you changed it, but rather, did you ever change it. Change your if
statement to
int x;
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
} else {
x = 0;
}
or
int x = isA() ? 1 :
isB() ? 2 : 0;
Your x
variable would have been effectively final it it was initialized once and not changed again under any circumstances. If you had only:
int x = 0;
doLater(() -> showErrorMessage(x));
then it would have compiled.
However, adding conditions that might change the variable's value
int x = 0;
if (isA()) {
x = 1;
} else if (isB()) {
x = 2;
}
makes the variable being not effectively final and thus the compile error is risen.
Additionally, since this pointer approach you've implemented wouldn't work, you could refactor your code a bit to a simple if-else statement:
if (isA()) {
doLater(() -> showErrorMessage(1));
} else if (isB()) {
doLater(() -> showErrorMessage(2));
}
and completely get rid of x
.
Short version, a variable is effectively final if it is assigned exactly once, no matter which code path is executed.
Long version, quoting Java Language Specification 4.12.4. final Variables (emphasis mine):
Certain variables that are not declared
final
are instead considered effectively final:
- A local variable whose declarator has an initializer (§14.4.2) is effectively final if all of the following are true:
- It is not declared
final
.- It never occurs as the left hand side in an assignment expression (§15.26). (Note that the local variable declarator containing the initializer is not an assignment expression.)
- It never occurs as the operand of a prefix or postfix increment or decrement operator (§15.14, §15.15).
Now, you can make it effectively final by removing the initializer, because it continues:
- A local variable whose declarator lacks an initializer is effectively final if all of the following are true:
- It is not declared
final
.- Whenever it occurs as the left hand side in an assignment expression, it is definitely unassigned and not definitely assigned before the assignment; that is, it is definitely unassigned and not definitely assigned after the right hand side of the assignment expression (§16 (Definite Assignment)).
- It never occurs as the operand of a prefix or postfix increment or decrement operator.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With