26

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?

Matt
  • 17,290
  • 7
  • 57
  • 71
Suzan Cioc
  • 29,281
  • 63
  • 213
  • 385

4 Answers4

36

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.

Matt
  • 17,290
  • 7
  • 57
  • 71
9

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

icza
  • 389,944
  • 63
  • 907
  • 827
2

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.

Jatin
  • 31,116
  • 15
  • 98
  • 163
2

Yes, it sort of is.

Basically they realised that the compiler already has to decide by analysing the code when a local variable is "effectively final", that is, its value is never changed.

So the semantics haven't changed: while there's no need to explicitly declare the variable final any more, you still have to make sure it is never reassigned.

biziclop
  • 48,926
  • 12
  • 77
  • 104
  • Just a side note: they *must* have known it all along---it is exactly the same analysis that had been churning out the `final`-related compiler errors since Java 1.0 :) What changed in the meantime is the *spirit of Java*---once a "blue-collar language" where boilerplate and verbosity were *features*, today a language with serious FP support and syntactic sugar all over. – Marko Topolnik Feb 09 '15 at 11:35
  • @MarkoTopolnik You're right, what they realised is more that they can rely on that alone and there's no reason to force developers to add the `final` keyword. But the answer had been staring them in the face from day one. – biziclop Feb 09 '15 at 12:59