java - 常规闭包内部如何工作?

标签 java groovy lambda closures internal

Java lambda 无法修改周围范围内的变量(不会关闭)。但为什么 Groovy 可以闭包呢?它的内部实现是怎样的?

例如,在这里,闭包为什么可以增加外部变量i?每次迭代都会创建一个内部对象吗?

for (int i = 0; i < n;) {
    { -> i++ }.run()
}

最佳答案

在本例中,i 被装箱在可变的 groovy 引用内。在 Java 中,设置 i 将更改此循环所在方法的局部变量。这就带来了一系列技术问题,即如果函数对象离开方法会发生什么。但在 groovy 中,i 驻留在堆上。

这个解释来 self 理解的字节码,你可以通过以下命令获得:

javap -c <name of closure class>

查看方法 doCall,首先查找仿函数的 CallSite 对象(由于 lambda 可以共享其类,因此调用站点基本上是捕获的局部变量的集合) ),并检索特定的 i:

10: getfield      #29                 // Field i:Lgroovy/lang/Reference;

如您所见,i的类型是groovy.lang.Reference。接下来,数字递增:

27: invokestatic  #51                 // Method org/codehaus/groovy/runtime/DefaultGroovyMethods.next:(Ljava/lang/Number;)Ljava/lang/Number;

之后,结果被加载回 groovy 引用:

42: invokevirtual #61                 // Method groovy/lang/Reference.set:(Ljava/lang/Object;)V

这类似于在 Java 中执行类似的操作:

for (AtomicInteger i = new AtomicInteger(); i.get() < n;) {
    ((Runnable) () ->  System.out.println(i.getAndIncrement())).run();
}

我使用 AtomicInteger 来表示驻留在堆上而不是局部变量槽中的可变 int

关于java - 常规闭包内部如何工作?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42873880/

相关文章:

java - 有没有更简洁的方法来使用 try-with-resource 和 PreparedStatement?

java - log4J 与 java applet 和 java 插件2

java - 写入我的 CipherStream 不会写入我的 ByteArrayOutputStream

java - postgresql存在检查错误,语法错误?

c++ - 使用可调用参数重载可调用

c++ - C++ Actor 系统的消息接收

java - 无法创建构建动物园的容器

excel - 如何使用 Groovy 编写 Excel 文件脚本

xml - 如何在使用 XmlSlurper 解析 xml 时读取带连字符的属性名称(例如 model_name)

Grails 配置文件返回空对象而不是字符串