Java 7 was saying "Cannot refer to the non-final local variable message defined in an enclosing scope" on following code:
public class Runner {
public static void main(String[] args) {
String message = "Hello world";
new Runnable() {
@Override
public void run() {
System.out.println(message);
}
}.run();
}
}
Java 8 does not.
Suspect this is about adding functional programming features to Java.
Does it process the code similarly?
A local inner class cannot be instantiated from outside the block where it is created in. Till JDK 7, the Local inner class can access only the final local variable of the enclosing block. However, From JDK 8, it is possible to access the non-final local variable of enclosing block in the local inner class.
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.
In simple terms, objects or primitive values are effectively final if we do not change their values after initialization. In the case of objects, if we do not change the reference of an object, then it is effectively final — even if a change occurs in the state of the referenced object.
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.
Java 8 implicitly makes message
final because it is never modified. Try modifying it anywhere in your code and you will get a compilation error (because this removes the implicit final
).
This is called effectively final. Quoting From the docs:
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. A variable or parameter whose value is never changed after it is initialized is effectively final.
Java 8 (and Lambdas) introduce the effectively final term: even though you didn't delcare it final with the final
keyword, if it is not modified, it is as good as final.
Quoting from Oracle Tutorial: Local Classes:
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. A variable or parameter whose value is never changed after it is initialized is effectively final.
Your message is effectively final so it is valid to refer to it from anonymous inner classes and lambdas.
If you change the value of the message, it will not be effectively final anymore:
String message = "Hello world";
new Runnable() {
@Override
public void run() {
System.out.println(message);
}
}.run();
message = "modified";
And therefore you get the following error (from Eclipse):
Local variable message defined in an enclosing scope must be final or effectively final
Or form javac
:
error: local variables referenced from an inner class must be final or effectively final
The variable message
is effectively final. Quoting from language reference
If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors.
Hence, because message
reference is not changed anywhere within you inner class, the compiler treats it as effectively final.
This would have thrown error:
new Runnable() {
@Override
public void run() {
message = "hey";
System.out.println(message);
}
}.run();
The reason, java7 compiler throws error is because of a spec change for lambdas.
Any local variable, formal parameter, or exception parameter used but not Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.
Anonymous Inner classes and lambdas share the same rules.
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