Java stopped erroring on non-final variables in inner classes (java 8) [duplicate]

扶醉桌前 提交于 2019-11-30 16:25:56

问题


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?


回答1:


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.




回答2:


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




回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/28408109/java-stopped-erroring-on-non-final-variables-in-inner-classes-java-8

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!