【问题标题】:Closures in JavaJava中的闭包
【发布时间】:2019-12-22 12:58:15
【问题描述】:

这个例子是在JavaScript中模拟闭包(我不懂JS):

public class Lambda {

    public static void main(String[] args) {
        Supplier generator = Lambda.generator();
        System.out.println(generator.get());
        System.out.println(generator.get());
        System.out.println(generator.get());
    }

    static Supplier<Integer> generator() {
        Integer arr[] = {0};
        return () -> ++arr[0];
    }
}

输出为 1 2 3。 通常局部方法变量的生命周期受方法执行时间的限制。但在这种情况下,对arr[] 的引用存储在某处。那么它存储在哪里以及引擎盖下是什么?

【问题讨论】:

    标签: java lambda closures


    【解决方案1】:

    Java 语言规范 (§15.27.4) 说:

    lambda 表达式的值是对具有以下属性的类实例的引用:

    • 该类实现了目标功能接口类型...

    因此 lambda 定义了一个类,就像匿名类声明所做的那样,并且 lambda 表达式导致对该类的实例的引用。对arr 的引用由属于该实例的合成final 字段保存。


    这是一个在 JShell REPL 中使用反射的示例,以演示具有合成字段的 lambda:

    > class A { static Runnable a(int x) { return () -> System.out.println(x); } }
    | created class A
    
    > Runnable r = A.a(5);
    r ==> A$$Lambda$15/1476394199@31ef45e3
    
    > import java.lang.reflect.Field;
    
    > Field f = r.getClass().getDeclaredFields()[0];
    f ==> private final int A$$Lambda$15/1476394199.arg$1
    
    > f.setAccessible(true);
    
    > f.get(r)
    $6 ==> 5
    

    【讨论】:

    • 谢谢,能不能也给个“合成终场”的参考?
    • 很难得到一个权威的,因为它更像是一个实现细节而不是一个指定的行为。 JVMS 定义了“合成”的含义,但没有具体说明它适用的所有情况:docs.oracle.com/javase/specs/jvms/se7/html/… 本文展示了一个反编译的 lambda,其中包含一个名为 cap$0 的合成字段:doanduyhai.com/blog/?p=1353 Java Closures and Lambda 一书 Robert Fischer (2015) 在第 152 页讨论了 lambdas 的合成字段。
    • 如果有帮助,我已经添加了一个示例,通过 REPL 中的反射显示合成字段。
    猜你喜欢
    • 2011-09-02
    • 2011-07-23
    • 2013-09-21
    • 2010-09-06
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2012-09-15
    • 2012-12-23
    相关资源
    最近更新 更多