I'm playing with lambdas in Java 8 and I came across warning local variables referenced from a lambda expression must be final or effectively final
. I know that when I use variables inside anonymous class they must be final in outer class, but still - what is the difference between final and effectively final?
... starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final.
For example, suppose that the variable numberLength
is not declared final, and you add the marked assignment statement in the PhoneNumber
constructor:
public class OutterClass {
int numberLength; // <== not *final*
class PhoneNumber {
PhoneNumber(String phoneNumber) {
numberLength = 7; // <== assignment to numberLength
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
...
}
...
}
Because of this assignment statement, the variable numberLength is not effectively final anymore. As a result, the Java compiler generates an error message similar to "local variables referenced from an inner class must be final or effectively final" where the inner class PhoneNumber tries to access the numberLength variable:
http://codeinventions.blogspot.in/2014/07/difference-between-final-and.html
http://docs.oracle.com/javase/tutorial/java/javaOO/localclasses.html
I find the simplest way to explain "effectively final" is to imagine adding the final
modifier to a variable declaration. If, with this change, the program continues to behave in the same way, both at compile time and at run time, then that variable is effectively final.
case k
requires a constant expression which could be a constant variable ("A constant variable is a final variable of primitive type or type String that is initialized with a constant expression" JLS 4.12.4) which is a special case of a final variable.
This variable below is final, so we can't change it's value once initialised. If we try to we'll get a compilation error...
final int variable = 123;
But if we create a variable like this, we can change it's value...
int variable = 123;
variable = 456;
But in Java 8, all variables are final by default. But the existence of the 2nd line in the code makes it non-final. So if we remove the 2nd line from the above code, our variable is now "effectively final"...
int variable = 123;
So.. Any variable that is assigned once and only once, is "effectively final".
According to the docs:
A variable or parameter whose value is never changed after it is initialized is effectively final.
Basically, if the compiler finds a variable does not appear in assignments outside of its initialization, then the variable is considered effectively final.
For example, consider some class:
public class Foo {
public void baz(int bar) {
// While the next line is commented, bar is effectively final
// and while it is uncommented, the assignment means it is not
// effectively final.
// bar = 2;
}
}
bar
in your example is not a local variaable, but a field. "Effectively final" in the error message as above does not apply to fields at all.
bar
is a parameter here, not a field.
'Effectively final' is a variable which would not give compiler error if it were to be appended by 'final'
From a article by 'Brian Goetz',
Informally, a local variable is effectively final if its initial value is never changed -- in other words, declaring it final would not cause a compilation failure.
lambda-state-final- Brian Goetz
A variable is final or effectively final when it's initialized once and it's never mutated in its owner class. And we can't initialize it in loops or inner classes.
Final:
final int number;
number = 23;
Effectively Final:
int number;
number = 34;
Note: Final and Effective Final are similar(Their value don't change after assignment) but just that effective Final variables are not declared with Keyword final.
When a lambda expression uses an assigned local variable from its enclosing space there is an important restriction. A lambda expression may only use local variable whose value doesn't change. That restriction is referred as "variable capture" which is described as; lambda expression capture values, not variables. The local variables that a lambda expression may use are known as "effectively final". An effectively final variable is one whose value does not change after it is first assigned. There is no need to explicitly declare such a variable as final, although doing so would not be an error. Let's see it with an example, we have a local variable i which is initialized with the value 7, with in the lambda expression we are trying to change that value by assigning a new value to i. This will result in compiler error - "Local variable i defined in an enclosing scope must be final or effectively final"
@FunctionalInterface
interface IFuncInt {
int func(int num1, int num2);
public String toString();
}
public class LambdaVarDemo {
public static void main(String[] args){
int i = 7;
IFuncInt funcInt = (num1, num2) -> {
i = num1 + num2;
return i;
};
}
}
Effective final topic is described in JLS 4.12.4 and the last paragraph consists a clear explanation:
If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors. Conversely, a local variable or parameter that is declared final in a valid program becomes effectively final if the final modifier is removed.
final is a variable declare with key word final
, example:
final double pi = 3.14 ;
it remains final
through out the program, changing pi after this line is never allowed.
effectively final : any local variable or parameter that is assigned a value only once right now(or updated only once). It may not remain effectively final throughout the program. so this means that effectively final variable might loses its effectively final property after immediately the time it gets assigned/updated at least one more assignment. example:
class EffectivelyFinal {
public static void main(String[] args) {
calculate(124,53);
}
public static void calculate( int operand1, int operand2){
int rem = 0; // operand1, operand2 and rem are effectively final here
rem = operand1%2 // rem lost its effectively final property here because it gets its second assignment
// operand1, operand2 are still effectively final here
class operators{
void setNum(){
operand1 = operand2%2; // operand1 lost its effectively final property here because it gets its second assignment
}
int add(){
return rem + operand2; // does not compile because rem is not effectively final
}
int multiply(){
return rem * operand1; // does not compile because both rem and operand1 are not effectively final
}
}
}
}
final
keyword to a declaration without introducing compile errors, then it is not effectively final. It is the contrapositive of this statement: "If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors."
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
// The following statement causes the compiler to generate
// the error "local variables referenced from a lambda expression
// must be final or effectively final" in statement A:
//
// x = 99;
}
}
}
As others have said, a variable or parameter whose value is never changed after it is initialized is effectively final. In the above code, if you change the value of x
in inner class FirstLevel
then the compiler will give you the error message:
Local variables referenced from a lambda expression must be final or effectively final.
If you could add the final modifier to a local variable, it was effectively final.
Lambda expressions can access
static variables,
instance variables,
effectively final method parameters, and
effectively final local variables.
Additionally,
An effectively final variable is a variable whose value is never changed, but it isn’t declared with the final keyword.
Source: Starting Out with Java: From Control Structures through Objects (6th Edition), Tony Gaddis
Furthermore, don't forget the meaning of final
that it is initialized exactly once before it is used for the first time.
Declaring a variable final
or not declaring it final
, but keeping it effectively final may result (depends on compiler) in different bytecode.
Let's have a look on a small example:
public static void main(String[] args) {
final boolean i = true; // 6 // final by declaration
boolean j = true; // 7 // effectively final
if (i) { // 9
System.out.println(i);// 10
}
if (!i) { // 12
System.out.println(i);// 13
}
if (j) { // 15
System.out.println(j);// 16
}
if (!j) { // 18
System.out.println(j);// 19
}
}
The corresponding bytecode of the main
method (Java 8u161 on Windows 64 Bit):
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iconst_1
3: istore_2
4: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
7: iconst_1
8: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
11: iload_2
12: ifeq 22
15: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_2
19: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
22: iload_2
23: ifne 33
26: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
29: iload_2
30: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
33: return
The corresponding line number table:
LineNumberTable:
line 6: 0
line 7: 2
line 10: 4
line 15: 11
line 16: 15
line 18: 22
line 19: 26
line 21: 33
As we see the source code at lines 12
, 13
, 14
doesn't appear in the byte code. That's because i
is true
and will not change it's state. Thus this code is unreachable (more in this answer). For the same reason the code at line 9
misses too. The state of i
doesn't have to be evaluated since it is true
for sure.
On the other hand though the variable j
is effectively final it's not processed in the same way. There are no such optimizations applied. The state of j
is evaluated two times. The bytecode is the same regardless of j
being effectively final.
The Effectively final variable is a local variable that is:
Not defined as final Assigned to ONLY once.
While a final variable is a variable that is:
declared with a final keyword.
However, starting in Java SE 8, a local class can access local variables and parameters of the >enclosing block that are final or effectively final.
This didn't start on Java 8, I use this since long time. This code used (before java 8) to be legal:
String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String ann = str; // <---- error, must be final (IDE's gives the hint);
String ann = strFin; // <---- legal;
String str = "legal statement on java 7,"
+"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl.";
//we are forced to use another name than str
}
);
final
variables can be accessed, but in Java 8 also those that are effectively final.
Success story sharing
numberLength
becomes a local variable of this method.