我在Java 8中使用lambda,我遇到警告,从lambda表达式引用的局部变量必须是final或有效的final。我知道当我在匿名类中使用变量时,它们在外部类中必须是final,但final和有效final之间的区别是什么?
当前回答
但是,从Java SE 8开始,局部类可以访问>外围块的局部变量和参数,这些变量和参数是final或有效的final。
这不是在Java 8开始的,我使用它很长时间了。 这段代码(在java 8之前)是合法的:
String str = ""; //<-- not accesible from anonymous classes implementation
final String strFin = ""; //<-- accesible
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String ann = str; // <---- error, must be final (IDE's gives the hint);
String ann = strFin; // <---- legal;
String str = "legal statement on java 7,"
+"Java 8 doesn't allow this, it thinks that I'm trying to use the str declared before the anonymous impl.";
//we are forced to use another name than str
}
);
其他回答
将变量声明为final或不声明为final,但有效地保持为final可能会导致(取决于编译器)在不同的字节码中。
让我们看一个小例子:
public static void main(String[] args) {
final boolean i = true; // 6 // final by declaration
boolean j = true; // 7 // effectively final
if (i) { // 9
System.out.println(i);// 10
}
if (!i) { // 12
System.out.println(i);// 13
}
if (j) { // 15
System.out.println(j);// 16
}
if (!j) { // 18
System.out.println(j);// 19
}
}
main方法对应的字节码(Windows 64 Bit上的Java 8u161):
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: istore_1
2: iconst_1
3: istore_2
4: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
7: iconst_1
8: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
11: iload_2
12: ifeq 22
15: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
18: iload_2
19: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
22: iload_2
23: ifne 33
26: getstatic #16 // Field java/lang/System.out:Ljava/io/PrintStream;
29: iload_2
30: invokevirtual #22 // Method java/io/PrintStream.println:(Z)V
33: return
对应的行号表:
LineNumberTable:
line 6: 0
line 7: 2
line 10: 4
line 15: 11
line 16: 15
line 18: 22
line 19: 26
line 21: 33
正如我们看到的,源代码在第12、13、14行没有出现在字节码中。这是因为i为真,不会改变它的状态。因此这段代码是不可达的(在这个答案中有更多内容)。出于同样的原因,第9行的代码也会出错。i的状态不需要求值,因为它是确定的。
另一方面,虽然变量j实际上是final,但它的处理方式不同。没有应用这样的优化。j的状态被求值两次。不管j是否有效为final,字节码都是相同的。
当一个变量被初始化一次并且在其所有者类中从未发生变化时,它就是最终的或有效的最终的。我们不能在循环或内部类中初始化它。
最后:
final int number;
number = 23;
有效的最后:
int number;
number = 34;
注意:Final和Effective Final是相似的(赋值后它们的值不会改变),但只是有效的Final变量 没有使用关键字final声明。
' effective final'是一个变量,如果它被'final'附加,则不会产生编译器错误
在“Brian Goetz”的一篇文章中,
非正式地说,如果局部变量的初始值从未改变,那么它实际上就是final变量——换句话说,声明它为final不会导致编译失败。
lambda-state-final- Brian Goetz
我发现解释“有效的final”最简单的方法是想象在变量声明中添加final修饰符。如果通过这个更改,程序在编译时和运行时继续以相同的方式运行,那么该变量实际上就是final。
如果可以将最后一个修饰符添加到局部变量,那么就是 有效的决赛。
Lambda表达式可以访问
静态变量, 实例变量, 有效的最终 方法参数,以及 有效的最终 局部变量。
来源:OCP: Oracle认证专业Java SE 8程序员II学习指南,Jeanne Boyarsky, Scott Selikoff
此外,
有效最终变量是一个值为never的变量 更改了,但没有使用final关键字声明。
来源:从Java开始:从控制结构到对象(第6版),Tony Gaddis
此外,不要忘记final的含义,它在第一次使用之前只初始化一次。
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- 在c#中,什么是单子?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder