Why is a local variable in Java not considered "virtually final", although nothing changes it?

In the 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 do not understand why it is causing a compilation error. The error says that x not final or actually final, therefore it cannot be accessed from the lambda body. After calling doLater after << 23> there is no change in x , so the value of x already defined when doLater is doLater .

I assume the answer to this question is that x cannot be called an effective final variable. However, I want to know what is the reason.

It is not possible for the compiler to create a temporary final variable by effectively creating the code:

 if (x != 0) { final int final_x = x; doLater(() -> showErrorMessage(final_x)); } 

and everything still works the same?

+7
java lambda java-8 final
source share
3 answers

Effectively final means that it could be made final , that is, it will never change. This means that effectively, the variable can be final .

The problem is that it does not track the last time you changed it, but rather, have you ever changed it. Change the if to

 int x; if (isA()) { x = 1; } else if (isB()) { x = 2; } else { x = 0; } 

or

 int x = isA() ? 1 : isB() ? 2 : 0; 
+15
source share

Your variable x would be permanently final, it would be initialized once, and under no circumstances would it be changed. If you only have:

 int x = 0; doLater(() -> showErrorMessage(x)); 

then it compiled.

However, adding conditions that can change the value of a variable

 int x = 0; if (isA()) { x = 1; } else if (isB()) { x = 2; } 

makes the variable ineffective final, and therefore the compilation error increases.


In addition, since this pointer approach that you implemented will not work, you can rebuild your code a bit into a simple if-else statement:

 if (isA()) { doLater(() -> showErrorMessage(1)); } else if (isB()) { doLater(() -> showErrorMessage(2)); } 

and completely get rid of x .

+4
source share

The short version, a variable is actually final if it is assigned exactly once , regardless of which code path is executed.

Long version citing Java Language specification 4.12.4. final variables (highlighted by me):

Some variables that are not declared final are actually considered final:

  • A local variable, the declarator has an initializer ( ยง14.4.2 ), is final if all of the following are true:
    • It is not declared final .
    • This never happens as the left side in an assignment expression ( ยง15.26 ). (Note that the local declarator variable containing the initializer is not an assignment expression.)
    • This never happens as the operand of a prefix or postfix increment or decrement operator ( ยง15.14 , ยง15.15 ).

Now you can do this permanently by removing the initializer, because it continues:

  • A local variable whose declarator does not have an initializer is indeed final if all of the following values:
    • It is not declared final .
    • Whenever this occurs as the left side in an assignment expression, it is definitely not assigned and definitely not assigned before the assignment ; that is, it is definitely not assigned and definitely not assigned after the right-hand side of the assignment expression ( ยง16 (Defined assignment) ).
    • This never happens as the operand of a prefix or postfix increment or decrement.
+2
source share

All Articles