Lambda中的变量捕获 [英] Variable capture in Lambda

查看:188
本文介绍了Lambda中的变量捕获的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我不认为为什么捕获的变量在lambda表达式中是final或有效的final.我查看了这个问题,但实际上并没有得到答案.

I can't think why the captured variables are final or effectively final in lambda expressions. I looked over this question and really quite didn't get the answer.

此变量捕获是什么?

当我寻找问题的解决方案时,我发现由于并发问题,这些变量是最终变量.但是对于这种情况,为什么我们不能使用reentrant lock对象将任务代码锁定在lambda中.

As I searched solutions for my problem, I read that these variables are final because of concurrency problems. But for such situation why can't we lock the task code in the lambda with a reentrant lock object.

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只是一个具有.)

In this particular code I want know the reasons why the last three statements gives an error, and why do we get to mutate Employee since it's a local variable.(Employee is just a class with getters and setters ofint i.)

我也想知道为什么我们也可以变异this.instance.

Also i like to know why we can mutate this.instance too.

对于上面提到的所有事实,我都提供了完整的详细答案.

I appreciate a full detailed answer on all facts I mentioned above.

推荐答案

我了解到,由于并发问题,这些变量是最终变量.

I read that these variables are final because of concurrency problems.

错误,这与并发无关,这完全取决于lambda(和匿名类)捕获" 变量值的方式.

Wrong, this has nothing to do with concurrency, it's all about how lambdas (and anonymous classes) "capture" variable values.

我想知道最后三个语句给出错误的原因

I want know the reasons why the last three statements gives an error

由于它们是捕获,因此它们必须是有效的最终确定.

Because they are captures, so they must be effectively final.

您真的不需要知道为什么内部需要这样做,只需接受您需要遵守该规则的事实即可.

You really don't need to know why the internals require this, just accept the fact that you need to adhere to that rule.

我想知道为什么我们可以变异this.instance

因为代码没有捕获 instance,所以它捕获了 this,而this是隐式最终的.

Because the code doesn't capture instance, it captures this, and this is implicitly final.

lambda主要是匿名类的语法糖.这不是真的,但是出于解释的目的,这是足够正确的,并且对于匿名类来说,该解释更容易理解.

A lambda is mostly syntactic sugar for an anonymous class. That's not really true, but for the purpose of this explanation, it's true enough, and the explanation is easier to understand for an anonymous class.

首先了解,JVM中没有匿名类之类的东西.实际上,也没有lambda表达式之类的东西,但这是另一回事.

First understand, there is no such thing as an anonymous class in the JVM. Actually, there is no such thing as a lambda expression either, but that's a different story.

但是,由于Java(该语言)具有匿名类,但是JVM没有,因此编译器必须通过将匿名类转换为内部类来伪造它. (仅供参考:内部类在JVM中也不存在,因此编译器也必须伪造该类.)

However, since Java (the language) has anonymous classes, but the JVM doesn't, the compiler has to fake it, by converting the anonymous class into an inner class. (FYI: Inner classes don't exist in the JVM either, so the compiler has to fake that too.)

让我们以示例方式进行此操作.说我们有以下代码:

Let's do this by example. Say we have this code:

// 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);

对于匿名类,编译器将生成这样的类:

For the anonymous class, the compiler will generate a class like this:

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);

通过复制已捕获"变量的值,捕获就是这样.

That's how capture works, by copying the value of the "captured" variable.

该变量根本没有被捕获,其值是,因为Java是构造函数调用中的按值传递.

The variable isn't captured at all, the value is, because Java is pass-by-value in the constructor call.

现在您可以争论,没有理由i应该有效地最终确定.当然,局部变量i和字段i现在是分开的,但是可以分别进行修改.

Now you can argue, that there is no reason why i should be effectively final. Sure, the local variable i and the field i are now separate, but they could be separately modified.

但这是有原因的,这确实是一个很好的理由. i已被复制且是单独的,这一事实被完全隐藏,并且是实现细节.程序员会经常忘记这一点,并认为它们是相同的,这将导致大量失败的代码,并提醒您浪费大量的调试时间.

But there is a reason, and it's a really good reason. The fact that i has been copied, and is separate, is entire hidden, and is an implementation detail. Programmers would constantly forget that, and think they are the same, which would lead to lots of failed code, and many wasted hours of debugging to be reminded of that.

为了清楚起见,必须- >捕获 ,并且匿名类中的ii相同,因为即使JVM无法做到这一点,Java语言也将其定义为它.

For code clarity, it must be as-if the i local variable was captured, and that the i in the anonymous class is the same as the i outside, because that is what the Java language defines it to be, even though the JVM can't do that.

要使其像这样出现,局部变量 必须 实际上必须是最终变量,因此(内部)变量不是' t 已捕获,对运行的代码没有任何影响.

To make it appear like that, the local variable MUST be effectively final, so the fact that (internally) the variable wasn't captured at all, makes no difference to the running code.

这篇关于Lambda中的变量捕获的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆