我在Java 8中使用lambda,我遇到警告,从lambda表达式引用的局部变量必须是final或有效的final。我知道当我在匿名类中使用变量时,它们在外部类中必须是final,但final和有效final之间的区别是什么?
当前回答
将变量声明为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,字节码都是相同的。
其他回答
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
// The following statement causes the compiler to generate
// the error "local variables referenced from a lambda expression
// must be final or effectively final" in statement A:
//
// x = 99;
}
}
}
正如其他人所说,在初始化后值从未改变的变量或参数实际上是final的。在上面的代码中,如果你在内部类FirstLevel中改变了x的值,那么编译器会给你一个错误消息:
从lambda表达式引用的局部变量必须是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
}
);
effective final变量是一个局部变量,即:
未定义为最终 只分配一次。
final变量是这样的变量:
用final关键字声明。
下面这个变量是最终的,所以我们不能改变它的值一旦初始化。如果我们尝试,我们会得到一个编译错误…
final int variable = 123;
但是如果我们创建一个这样的变量,我们可以改变它的值…
int variable = 123;
variable = 456;
但是在Java 8中,默认情况下所有变量都是final。但是代码中第二行的存在使得它不是最终的。因此,如果我们从上面的代码中删除第二行,我们的变量现在是“有效的final”…
int variable = 123;
所以. .任何赋值一次且仅一次的变量都是“有效的最终变量”。
根据文件:
在初始化后值从未改变的变量或参数实际上是final。
基本上,如果编译器发现一个变量在初始化之外没有出现在赋值中,则认为该变量实际上是final变量。
例如,考虑一些类:
public class Foo {
public void baz(int bar) {
// While the next line is commented, bar is effectively final
// and while it is uncommented, the assignment means it is not
// effectively final.
// bar = 2;
}
}
推荐文章
- 在Java中使用正则表达式提取值
- 如何允许所有网络连接类型HTTP和HTTPS在Android(9)馅饼?
- Intellij IDEA Java类在保存时不能自动编译
- 何时使用Mockito.verify()?
- 在maven中安装mvn到底做什么
- 不可变与不可修改的集合
- 如何在JSON中使用杰克逊更改字段名
- GSON -日期格式
- 如何从线程捕获异常
- 无法解析主机"<URL here>"没有与主机名关联的地址
- 如何在Java中打印二叉树图?
- String.format()在Java中格式化双重格式
- com.jcraft.jsch.JSchException: UnknownHostKey
- Java中的操作符重载
- 如何加速gwt编译器?