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
,但你仍然必须确保它永远不会被重新分配。