Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is a local variable in Java not considered "effectively final" even though nothing modifies it afterwards?

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?

like image 660
Randy Sugianto 'Yuku' Avatar asked Feb 23 '16 08:02

Randy Sugianto 'Yuku'


People also ask

Can local variables be final in Java?

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.

Why should local variables be final or effectively 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.

How do you make a local variable effectively final?

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.

Can local variables be declared 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.


3 Answers

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;
like image 97
Peter Lawrey Avatar answered Oct 16 '22 23:10

Peter Lawrey


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.

like image 38
Konstantin Yovkov Avatar answered Oct 16 '22 23:10

Konstantin Yovkov


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.
like image 1
Andreas Avatar answered Oct 16 '22 22:10

Andreas