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引用似乎是一個隱藏的實現細節。 –