This question already has an answer here:
这个问题在这里已有答案:
- Difference between final and effectively final 11 answers
- 最终和有效最终11个答案之间的差异
Java 7 was saying "Cannot refer to the non-final local variable message defined in an enclosing scope" on following code:
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 does not.
Java 8没有。
Suspect this is about adding functional programming features to Java.
怀疑这是关于向Java添加函数式编程功能。
Does it process the code similarly?
它是否同样处理代码?
4 个解决方案
#1
26
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
).
Java 8隐式地使消息最终,因为它永远不会被修改。尝试在代码中的任何位置修改它,您将收到编译错误(因为这会删除隐式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.
但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。在初始化之后其值永远不会改变的变量或参数实际上是最终的。
#2
7
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.
Java 8(和Lambdas)引入了有效的最终术语:即使你没有使用final关键字对它进行最终删除,如果它没有被修改,它就像final一样好。
Quoting from Oracle Tutorial: Local Classes:
引用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.
但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。在初始化之后其值永远不会改变的变量或参数实际上是最终的。
Your message is effectively final so it is valid to refer to it from anonymous inner classes and lambdas.
您的消息实际上是最终的,因此从匿名内部类和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):
因此,您会收到以下错误(来自Eclipse):
Local variable message defined in an enclosing scope must be final or effectively final
在封闭范围内定义的局部变量消息必须是最终的或有效的最终消息
Or form javac
:
或者形成javac:
error: local variables referenced from an inner class must be final or effectively final
错误:从内部类引用的局部变量必须是最终的或有效的最终
#3
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.
如果变量实际上是final,则将final修饰符添加到其声明中不会引入任何编译时错误。
Hence, because message
reference is not changed anywhere within you inner class, the compiler treats it as effectively final.
因此,因为消息引用不会在内部类中的任何位置更改,所以编译器将其视为有效的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.
原因是,java7编译器抛出错误是因为lambdas的spec更改。
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.
使用的任何局部变量,形式参数或异常参数但未使用但未在lambda表达式中声明的任何局部变量,形式参数或异常参数必须声明为final或者是有效的final(§4.12.4),或者是编译尝试使用时发生时间错误。
Anonymous Inner classes and lambdas share the same rules.
匿名内部类和lambdas共享相同的规则。
#4
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.
所以语义没有改变:虽然不再需要显式声明变量final,但你仍然必须确保它永远不会被重新分配。
#1
26
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
).
Java 8隐式地使消息最终,因为它永远不会被修改。尝试在代码中的任何位置修改它,您将收到编译错误(因为这会删除隐式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.
但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。在初始化之后其值永远不会改变的变量或参数实际上是最终的。
#2
7
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.
Java 8(和Lambdas)引入了有效的最终术语:即使你没有使用final关键字对它进行最终删除,如果它没有被修改,它就像final一样好。
Quoting from Oracle Tutorial: Local Classes:
引用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.
但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。在初始化之后其值永远不会改变的变量或参数实际上是最终的。
Your message is effectively final so it is valid to refer to it from anonymous inner classes and lambdas.
您的消息实际上是最终的,因此从匿名内部类和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):
因此,您会收到以下错误(来自Eclipse):
Local variable message defined in an enclosing scope must be final or effectively final
在封闭范围内定义的局部变量消息必须是最终的或有效的最终消息
Or form javac
:
或者形成javac:
error: local variables referenced from an inner class must be final or effectively final
错误:从内部类引用的局部变量必须是最终的或有效的最终
#3
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.
如果变量实际上是final,则将final修饰符添加到其声明中不会引入任何编译时错误。
Hence, because message
reference is not changed anywhere within you inner class, the compiler treats it as effectively final.
因此,因为消息引用不会在内部类中的任何位置更改,所以编译器将其视为有效的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.
原因是,java7编译器抛出错误是因为lambdas的spec更改。
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.
使用的任何局部变量,形式参数或异常参数但未使用但未在lambda表达式中声明的任何局部变量,形式参数或异常参数必须声明为final或者是有效的final(§4.12.4),或者是编译尝试使用时发生时间错误。
Anonymous Inner classes and lambdas share the same rules.
匿名内部类和lambdas共享相同的规则。
#4
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.
所以语义没有改变:虽然不再需要显式声明变量final,但你仍然必须确保它永远不会被重新分配。