我想不出为什么捕获的变量在lambda表达式中是final或有效的final。我查看了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
进行突变,因为它是局部变量。(Employee只是具有int i
的getter和setter的类。 。)
而且我也想知道为什么我们也可以变异this.instance
。
我感谢我在上面提到的所有事实的详细答复。
错误,这与并发无关,这与lambda(和匿名类)“ capture”变量值的方式有关。
我想知道最后三个语句给出错误的原因
因为它们是捕获,所以它们
必须有效地是最终的。
您真的不需要知道内部为什么要这样做,只需接受您需要遵守该规则的事实。我想知道为什么我们可以变异
this.instance
因为代码不是capture
instance
,所以它是captures
原因this
,并且this
是隐式最终的。
lambda主要是匿名类的语法糖。这不是真的,但出于解释的目的,这是足够正确的,对于匿名类,该解释更容易理解。首先了解,JVM中没有匿名类之类的东西。实际上,也没有lambda表达式之类的东西,但这是另一回事。
但是,由于Java具有匿名类,因此编译器必须通过将匿名类转换为静态嵌套类来伪造它。
让我们以身作则。说我们有以下代码:
// 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);
对于匿名类,编译器将生成这样的类:
static 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);
这就是
capture的工作方式,通过copying“ captured”变量的值。
变量根本不是captured,其值是,因为Java是构造函数调用中的按值传递。现在您可以争论,没有理由为什么
但是有,这是一个很好的理由。i
应该是有效的最终值。当然,局部变量i
和字段i
现在是分开的,但是可以分别修改。i
已被复制并且是分开的事实被完全隐藏,并且是实现细节。程序员会经常忘记这一点,并认为它们是相同的,这将导致许多失败的代码,并提醒您浪费大量的调试时间。为了清楚起见,如果
i
局部变量为captured
,并且匿名类中的i
与外部的i
为same,则必须为[]。 >要使其像这样[
实际上是最终的,因此(内部)变量根本没有被捕获的事实与变量没有区别。运行代码。,局部变量 MUST