我正在使用Java 8中的lambda表达式,并遇到警告本地变量从lambda表达式引用必须是final或有效 final
。我知道当我在匿名类中使用变量时,它们必须在外部类中是final的,但仍然存在问题-什么是final和有效final之间的区别?
我正在使用Java 8中的lambda表达式,并遇到警告本地变量从lambda表达式引用必须是final或有效 final
。我知道当我在匿名类中使用变量时,它们必须在外部类中是final的,但仍然存在问题-什么是final和有效final之间的区别?
final
修饰符添加到局部变量中,则该变量就是“有效地final”。Lambda表达式可以访问静态变量、实例变量、有效的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
的相应字节码(Java 8u161,Windows 64位):
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
是true
,并且不会改变其状态。因此,这段代码是无法访问的(更多信息请参见answer)。由于同样的原因,第9
行的代码也缺失了。不必评估i
的状态,因为它肯定是true
。
另一方面,尽管变量j
是有效地最终的,但处理方式不同。没有应用这样的优化。j
的状态被评估了两次。无论j
是否为有效地最终,字节码都是相同的。
有效终态变量是一个本地变量,它:
final
而 final 变量是一个被声明为:
final
关键字的变量。List<String> ls = Arrays.asList("A", "B", "C");
for (Iterator<String> iterator = ls.iterator(); iterator.hasNext();) {
String s = (String) iterator.next();
System.out.println(s);
}
这里变量iterator是有效地终态,因为它遵循上述规则。
注意:如果变量是有效地终态,也可以使用final
关键字声明。
List<String> ls = Arrays.asList("A", "B", "C");
for (final Iterator<String> iterator = ls.iterator(); iterator.hasNext();) {
String s = (String) iterator.next();
System.out.println(s);
}
ex: 模式声明的局部变量。
public String getStringfromObj(Object o){
if (o instanceof String s && s.startsWith("Java")) { // can be declared as final String s
return s;
}
return "";
}
final
声明示例:
List<String> ls = Arrays.asList("A", "B", "C");
int k; // can be declared as final int k
if ((k = ls.size()) > 0) {
System.out.println(k);
}
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
}
);