Java 停止在内部 类 (java 8) 中的非最终变量上出错

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

Java 7 在以下代码中说 "Cannot refer to the non-final local variable message defined in an enclosing scope":

public class Runner {   
    public static void main(String[] args) {

        String message = "Hello world";

        new Runnable() {
            @Override
            public void run() {
                System.out.println(message);
            }
        }.run();
    }
}

Java8个没有。

怀疑这是向 Java 添加函数式编程功能。

它是否以类似的方式处理代码?

Java 8 隐含地使 message 最终,因为它永远不会被修改。尝试在代码中的任何地方修改它,你会得到一个编译错误(因为这会删除隐式 final)。

这称为有效最终。引用 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(和 Lambdas)引入了 effectively final 术语:即使您没有使用 final 关键字对其进行 final 处理,如果没有修改,和final一样好

引用自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.

您的消息实际上是最终消息,因此从匿名内部 类 和 lambdas 引用它是有效的。

如果您更改消息的值,它将不再是有效最终

String message = "Hello world";
new Runnable() {
    @Override
    public void run() {
        System.out.println(message);
    }
}.run();
message = "modified";

因此您会收到以下错误(来自 Eclipse):

Local variable message defined in an enclosing scope must be final or effectively final

或形式javac:

error: local variables referenced from an inner class must be final or effectively final

变量messageeffectively final。引用自语言参考

If a variable is effectively final, adding the final modifier to its declaration will not introduce any compile-time errors.

因此,因为 message 引用在您内部 class 中的任何地方都没有改变,编译器将其视为有效的最终引用。

这会引发错误:

new Runnable() {
            @Override
            public void run() {
                message = "hey";
                System.out.println(message);
            }
        }.run();

java7 编译器抛出错误的原因是 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 和 lambda 共享相同的规则。

是的,有点像。

基本上他们意识到编译器已经必须通过分析代码来决定什么时候局部变量是"effectively final",即它的值永远不会改变。

因此语义没有改变:虽然不再需要显式声明变量 final,但您仍然必须确保它永远不会被重新分配。