Lambda 捕获的变量是否保证在 Lambda 的整个生命周期中存活?[复制]

Are Lambda Captured Variables Guarenteed To Survive Entire life of Lambda? [duplicate]

提问人:Gonen I 提问时间:9/19/2021 最后编辑:Gonen I 更新时间:9/19/2021 访问量:60

问:

这个问题在这里已经有答案了:
2年前关闭。

社区在 2 年前审查了是否重新打开这个问题,并将其关闭:

重复这个问题已经得到回答,不是唯一的,也没有与另一个问题区分开来。

在 Java 中,捕获的 lambda 变量的值可以在原始变量的作用域之外使用,如下所示:

interface MyInterface{
    void doSomething(  );
}

class Example {
 MyInterface lambda;
 public static void main(String[] args) {
     Example e = new Example();
     e.f1();
     e.f2();
 }
 void f1() {
     int local = 777;
     lambda = () -> System.out.println(local);
 }
 void f2() {
     lambda.doSomething(); // prints 777
 }
}

匿名类也可以做到这一点。我怀疑实现会通过复制捕获本地值来做到这一点。

规范是否保证捕获的本地人能够像 lambda 一样长时间地生存?

我很想参考文档中的相关段落。(我搜索了一下,没有找到)

Java Lambda 闭包

评论

1赞 Hovercraft Full Of Eels 9/19/2021
我现在明白了。你看过编译后的代码吗?这可能会告诉你一些事情。
0赞 Gonen I 9/19/2021
@HovercraftFullOfEels OK。由于我对字节码的掌握很差,看起来肯定像是 777 的副本被加载到 f1 中返回对象的堆栈中。但这可能只是一个实现细节。我一直在寻找更正式的东西。
0赞 Turing85 9/19/2021
"规范是否保证捕获的本地人可以像 lambda 一样长时间地生存?-是的。我不知道概述它的确切 JLS 段落,但这至少在一定程度上是为什么在 lambda 中访问的变量必须或有效。finalfinal

答:

1赞 rzwitserloot 9/19/2021 #1

我怀疑实现会通过复制捕获本地值来做到这一点。

正确。因为如果捕获的本地变量被修改,这将非常令人困惑,除非捕获的变量被标记为 [A] 或 [B] “有效最终”,否则将拒绝编译您的代码,这在规范中,这意味着:您可以将其标记为最终。javacfinal

只要需要,此副本就会保留。一般来说,这些文档会保证除非你涉及 JNI,否则你无法在 java 中获得核心转储,而且垃圾回收规范不会比这更进一步。

1赞 Sweeper 9/19/2021 #2

6.5.6.1中,据说(强调我的)

如果声明声明了一个最终变量,该变量在简单表达式之前明确赋值,则名称的含义是该变量的。否则,表达式名称的含义是声明声明的变量。

因此,在您的 lambda 中表示 777,即您分配给 的(最终)值。这是通过复制您正确怀疑的变量来实现的。局部变量是否“存活”在这里并不重要。locallocal

根据另一节,局部变量无法“生存”(4.12.3):

当局部变量的声明不再在作用域中时,局部变量将不复存在。

当您在 中运行 lambda 时,局部变量的声明超出了范围,因此它“不复存在”。f2