Lambda 中的变量捕获
Variable capture in Lambda
我不明白为什么捕获的变量在 lambda 表达式中是最终的或实际上是最终的。我查看了 this question 并没有得到答案。
这个变量捕获是什么?
当我搜索我的问题的解决方案时,我读到这些变量是最终的,因为并发问题。但是对于这种情况,为什么我们不能用 reentrant lock
对象将任务代码锁定在 lambda 中。
public class Lambda {
private int instance=0;
public void m(int i,String s,Integer integer,Employee employee) {
ActionListener actionListener = (event) -> {
System.out.println(i);
System.out.println(s);
System.out.println(integer);
System.out.println(employee.getI());
this.instance++;
employee.setI(4);
integer++;//error
s="fghj";//error
i++;//error
};
}
}
在这个特定的代码中,我想知道最后三个语句出错的原因,以及为什么我们要改变 Employee
因为它是一个局部变量。(员工只是一个 class getters 和 setters of int i
.)
我也想知道为什么我们也可以变异 this.instance
。
我感谢对我上面提到的所有事实的完整详细回答。
I read that these variables are final because of concurrency problems.
错了,这与并发无关,它是关于lambdas(和匿名classes)如何"capture"变量值。
I want know the reasons why the last three statements gives an error
因为它们是 捕获,所以它们必须有效地最终。
您真的不需要知道为什么内部需要这个,只需接受您需要遵守该规则的事实。
i like to know why we can mutate this.instance
因为代码不捕获 instance
,它捕获 this
,并且this
是隐式最终的。
原因
lambda 主要是匿名 class 的语法糖。这不是真的,但为了这个解释的目的,它已经足够了,并且对于匿名 class.
的解释更容易理解
首先要明白,JVM中没有匿名class这种东西。实际上,也没有lambda表达式这样的东西,但那是另外一回事了。
但是,由于 Java(语言)有匿名的 classes,而 JVM 没有,编译器必须伪造它,将匿名的 class 转换为内部 class。 (仅供参考:内部 classes 也不存在于 JVM 中,因此编译器也必须伪造它。)
让我们举个例子。假设我们有这个代码:
// As anonymous class
int i = 0;
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println(i);
}
}
// As lambda expression:
int i = 0;
Runnable run = () -> System.out.println(i);
对于匿名class,编译器会生成这样的class:
final class Anon_1 implements Runnable {
private final int i;
Anon_1(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(i);
}
}
然后将代码编译为:
int i = 0;
Runnable run = new Anon_1(i);
这就是 捕获 的工作方式,通过 复制 "captured" 变量的值。
变量根本没有捕获,值是,因为Java在构造函数调用中是按值传递的。
现在您可以争辩说,i
没有理由应该有效地成为最终的。当然,局部变量i
和字段i
现在是分开的,但是可以单独修改。
但这是有原因的,而且是一个很好的理由。 i
已被复制,并且是独立的,是完全隐藏的,并且是一个实现细节。程序员会经常忘记这一点,并认为它们是相同的,这会导致大量失败的代码,并浪费许多调试时间来提醒这一点。
为了代码清晰,as-if i
局部变量 captured,并且[匿名 class 中的 =17=] 与外部的 i
相同 ,因为那是 Java 语言定义的内容,尽管 JVM 无法做到这一点。
为了让它出现那样,局部变量必须是有效的final,所以(内部)变量根本没有 captured 这一事实对 运行 代码没有影响。
我不明白为什么捕获的变量在 lambda 表达式中是最终的或实际上是最终的。我查看了 this question 并没有得到答案。
这个变量捕获是什么?
当我搜索我的问题的解决方案时,我读到这些变量是最终的,因为并发问题。但是对于这种情况,为什么我们不能用 reentrant lock
对象将任务代码锁定在 lambda 中。
public class Lambda {
private int instance=0;
public void m(int i,String s,Integer integer,Employee employee) {
ActionListener actionListener = (event) -> {
System.out.println(i);
System.out.println(s);
System.out.println(integer);
System.out.println(employee.getI());
this.instance++;
employee.setI(4);
integer++;//error
s="fghj";//error
i++;//error
};
}
}
在这个特定的代码中,我想知道最后三个语句出错的原因,以及为什么我们要改变 Employee
因为它是一个局部变量。(员工只是一个 class getters 和 setters of int i
.)
我也想知道为什么我们也可以变异 this.instance
。
我感谢对我上面提到的所有事实的完整详细回答。
I read that these variables are final because of concurrency problems.
错了,这与并发无关,它是关于lambdas(和匿名classes)如何"capture"变量值。
I want know the reasons why the last three statements gives an error
因为它们是 捕获,所以它们必须有效地最终。
您真的不需要知道为什么内部需要这个,只需接受您需要遵守该规则的事实。
i like to know why we can mutate
this.instance
因为代码不捕获 instance
,它捕获 this
,并且this
是隐式最终的。
原因
lambda 主要是匿名 class 的语法糖。这不是真的,但为了这个解释的目的,它已经足够了,并且对于匿名 class.
的解释更容易理解首先要明白,JVM中没有匿名class这种东西。实际上,也没有lambda表达式这样的东西,但那是另外一回事了。
但是,由于 Java(语言)有匿名的 classes,而 JVM 没有,编译器必须伪造它,将匿名的 class 转换为内部 class。 (仅供参考:内部 classes 也不存在于 JVM 中,因此编译器也必须伪造它。)
让我们举个例子。假设我们有这个代码:
// As anonymous class
int i = 0;
Runnable run = new Runnable() {
@Override
public void run() {
System.out.println(i);
}
}
// As lambda expression:
int i = 0;
Runnable run = () -> System.out.println(i);
对于匿名class,编译器会生成这样的class:
final class Anon_1 implements Runnable {
private final int i;
Anon_1(int i) {
this.i = i;
}
@Override
public void run() {
System.out.println(i);
}
}
然后将代码编译为:
int i = 0;
Runnable run = new Anon_1(i);
这就是 捕获 的工作方式,通过 复制 "captured" 变量的值。
变量根本没有捕获,值是,因为Java在构造函数调用中是按值传递的。
现在您可以争辩说,i
没有理由应该有效地成为最终的。当然,局部变量i
和字段i
现在是分开的,但是可以单独修改。
但这是有原因的,而且是一个很好的理由。 i
已被复制,并且是独立的,是完全隐藏的,并且是一个实现细节。程序员会经常忘记这一点,并认为它们是相同的,这会导致大量失败的代码,并浪费许多调试时间来提醒这一点。
为了代码清晰,as-if i
局部变量 captured,并且[匿名 class 中的 =17=] 与外部的 i
相同 ,因为那是 Java 语言定义的内容,尽管 JVM 无法做到这一点。
为了让它出现那样,局部变量必须是有效的final,所以(内部)变量根本没有 captured 这一事实对 运行 代码没有影响。