0
Java lambda无法修改周围作用域中的变量(不会关闭)。但是Groovy如何关闭?它的内部实现是什么?Groovy封闭如何在内部工作?
例如,在这里,如何关闭可以增加外部变量i
?是否会为每次迭代创建一个内部对象?
for (int i = 0; i < n;) {
{ -> i++ }.run()
}
Java lambda无法修改周围作用域中的变量(不会关闭)。但是Groovy如何关闭?它的内部实现是什么?Groovy封闭如何在内部工作?
例如,在这里,如何关闭可以增加外部变量i
?是否会为每次迭代创建一个内部对象?
for (int i = 0; i < n;) {
{ -> i++ }.run()
}
在这种情况下,i
是盒装在一个可变的groovy参考。在Java中,设置i
将更改此循环所在方法的局部变量。这引发了一堆关于如果函数对象离开方法会发生什么的技术问题。但在常规中,i
驻留在堆上。
这解释来自我所理解的字节码,您可以用命令得到:
javap -c <name of closure class>
望着方法doCall
,首先CallSite
对象的仿函数抬头(因为lambda表达式可以分享他们的班,调用点基本上都是捕获局部变量的集合),具体i
是retreived:
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;
之后,结果被装载回常规参考在:
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
,它驻留在堆上,而不是局部变量槽中。
谢谢。你有证据吗?如果我做'println i.class',我会看到这个动作吗?此外,Groovy何时知道它必须包装它?编译还是运行时?在这方面呢'@ComileStatic'呢? –
@ArtemNovikov证明是在仿函数的字节码中。起初我没有包括它,因为我无法将Groovy锅炉板与实际实施分离。我已尽最大努力。我不熟悉groovy,我只是碰巧知道如何读字节码。也许你可以自己测试一些东西。从我自己的测试看来,groovy似乎总是将'int'包装在一个常规的引用中,并且'@ CompileStatic'不会改变它。 'println i.class'将显示'java.lang.Integer',groovy引用似乎是一个隐藏的实现细节。 –