Java停止了内部类中非final变量的错误(java 8)

Java 7在下面的代码中说“不能引用封闭范围中定义的非最终局部变量消息”:

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没有。

怀疑这是关于向Java添加函数式编程function。

它是否同样处理代码?

Java 8隐式地使message最终,因为它永远不会被修改。 尝试在代码中的任何位置修改它,您将收到编译错误(因为这会删除隐式final )。

这被称为有效的最终 。 引用文档 :

但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。 在初始化之后其值永远不会改变的变量或参数实际上是最终的。

Java 8(和Lambdas)引入了有效的最终术语:即使你没有使用final关键字对它进行最终删除,如果它没有被修改,它就像final一样好。

引用Oracle Tutorial:Local Classes :

但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。 在初始化之后其值永远不会改变的变量或参数实际上是最终的。

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

如果您更改了邮件的值,它将不再是有效的最终版本

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

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

在封闭范围内定义的局部变量消息必须是最终的或有效的最终消息

或者形成javac

错误:从内部类引用的局部变量必须是最终的或有效的最终

变量message实际上是最终的 。 引用语言参考

如果变量实际上是final,则将final修饰符添加到其声明中不会引入任何编译时错误。

因此,因为message引用不会在内部类中的任何位置更改,所以编译器将其视为有效的final。

这会抛出错误:

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

原因是,java7编译器抛出错误是因为lambdas的spec更改。

使用的任何局部变量,forms参数或exception参数但未使用但未在lambda表达式中声明的任何局部变量,forms参数或exception参数必须声明为final或者是有效的final(§4.12.4),或者是编译尝试使用时发生时间错误。

匿名内部类和lambdas共享相同的规则。

是的,有点像。

基本上他们意识到编译器已经必须通过在局部变量“有效最终”时分析代码来决定,也就是说,它的值永远不会改变。

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