我无法理解为什么java语言的实现者为什么在lambda中使用并从函数作用域传递的变量必须是最终的。
我反编译了这段代码:
public class Main {
@FunctionalInterface
interface Test {
void method(int t);
}
static void test(Test t) {
t.method(3);
}
public static void main(String... args) {
int a = 3;
test((i)-> {
System.out.println(a + i);
});
}
}
以及编译器所做的是复制该变量,就好像它是通过构造函数传递一样。我得到了这3个课程:
1:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import Main.1;
import Main.Test;
public class Main {
public Main() {
}
static void test(Test var0) {
var0.method(3);
}
public static void main(String... var0) {
byte var1 = 3;
test(new 1(var1));
}
}
2:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import Main.Test;
final class Main$1 implements Test {
Main$1(int var1) {
this.val$a = var1;
}
public void method(int var1) {
System.out.println(this.val$a + var1);
}
}
3:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
@FunctionalInterface
interface Main$Test {
void method(int var1);
}
为什么实施者无法复制变量而不管它是否被修改,所以我们可以这样做:
public class Main {
@FunctionalInterface
interface Test {
void method(int t);
}
static void test(Test t) {
t.method(3);
}
public static void main(String... args) {
int a = 3;
test((i)-> {
a += 1; // allow modification, a is copied anyway, why not?
System.out.println(a + i);
});
}
}
答案 0 :(得分:7)
这没有技术原因。只是如果你允许在lambda中使用非final字段,那么你可以编写看起来很好但实际上不起作用的代码。
例如:
void printSum(Collection<Integer> numbers) {
int sum = 0;
numbers.forEach(i -> sum += i);
System.out.println(sum);
}
目前,编译器不允许您这样做,因为您无法访问lambda中的非最终sum
。正如您所指出的那样,变量无论如何都会被复制到lambda中,所以它可以允许它。
如果确实如此,则此代码将编译,但始终打印0,因为只修改了lambda中的sum
- 副本而不是“真实的”。
只允许“有效最终”变量被引用是一个很好的折衷方案,不需要final
关键字无处不在,同时仍然避免像这样的误解。