【问题标题】:Variable capture in LambdaLambda 中的变量捕获
【发布时间】:2020-04-07 08:02:01
【问题描述】:

我想不出为什么捕获的变量在 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,因为它是一个局部变量。(Employee 只是一个具有@987654325 的getter 和setter 的类@.)

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

感谢对我上面提到的所有事实的完整详细回答。

【问题讨论】:

  • thisemployee 也是最终版本。您不能重新分配它们:employee = new Employee(); 也将是一个错误。

标签: java lambda final


【解决方案1】:

我读到这些变量是最终的,因为并发问题。

错了,这与并发无关,都是关于 lambdas(和匿名类)“捕获”变量值的方式。

我想知道最后三个语句报错的原因

因为它们是捕获,所以它们必须是有效的最终

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

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

因为代码没有捕获 instance,它捕获 this,并且this 是隐含的final。


原因

lambda 主要是匿名类的语法糖。这不是真的,但是为了这个解释的目的,它已经足够真实了,并且对于匿名类来说,这个解释更容易理解。

首先要明白,JVM中没有匿名类这种东西。实际上,也没有 lambda 表达式这样的东西,但那是另一回事。

但是,由于 Java(该语言)具有匿名类,而 JVM 没有,因此编译器必须通过将匿名类转换为内部类来伪造它。 (仅供参考: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);

对于匿名类,编译器会生成这样一个类:

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 就是这样工作的,通过复制“捕获”变量的值。

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

现在您可以争辩说,i 没有理由成为有效的最终结果。当然,局部变量i 和字段i 现在是分开的,但它们可以单独修改。

但这是有原因的,而且这是一个非常好的理由。 i 已被复制,并且是独立的,完全隐藏的事实,并且是一个实现细节。程序员会经常忘记这一点,并认为它们是相同的,这会导致大量代码失败,并且需要提醒许多调试时间。

为了代码清晰,必须好像i局部变量被捕获,并且匿名类中的i与外部的i 相同,因为这是 Java 语言对其的定义,即使 JVM 无法做到这一点。

要使其显示那样,局部变量必须实际上是最终的,因此(内部)变量不是t 捕获,对运行代码没有影响。

【讨论】:

  • 优秀的答案。
  • 先生,您解释得真好,所有要点都解释清楚了,非常感谢!
猜你喜欢
  • 1970-01-01
  • 2013-11-26
  • 2011-11-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多