JavaFX のローカル変数の面白げな側面(1)クロージャ (closure)

昨日 クロージャについて 書いたけど、下のエントリをしっかり読んだら結構興味深いことが書かれていたので、再提起。
Local Variable Extent in JavaFX | Synchronous Messages


JavaFXクロージャ (closure) の例をもう一度掲載。


ソース

function f(p: Integer): function(): Integer {
    var localvar = p;
    function(): Integer {
        ++localvar;
    }
}

var g = f(17);
var h = f(32);

println("g() => {g()}");
println("h() => {h()}");
println("g() => {g()}");
println("h() => {h()}");


実行結果

g() => 18
h() => 33
g() => 19
h() => 34

Synchronous Messages には上記のサンプルコードと、次のようなことが書かれている。

It’s not clear whether the functions “really” are different. The compiler might generate and use the same code for them, but they definitely use different environments. If you compare g and h you’ll find that they are not equal. Indeed, calling them gives different results. As you can see from the output, they clearly contain different instances of the local variable localvar. What’s more, these two instances of localvar exist long after f has returned. And they’ll continue to exist for as long as you hold onto g and h.

Local Variable Extent in JavaFX | Synchronous Messages

意味はこんな感じ。(しかし、そもそも訳の正確性には注意されたし!)
(2つの内部関数 d と h が)本当に(実体が)異なるものかどうかははっきりとしていない。コンパイラはそれらの(関数の)為に、(新しいコードを)生成するかもしれないし、同じコードを用いるかもしれないが、それらは確実に異なる環境(スコープかコンテキストの方が理解しやすい?)を使う。g と h を比較してみれば、それら(関数)が同じものでないことが解るだろう。確かに呼び出しは異なる結果を得る。出力から解るように、それら(関数)は明らかにローカル変数 localvar の異なるインスタンスを含んでいる。その上、それらの二つの localvar のインスタンスは f が返した後もずっと存在していて、g と h に保持している間、存在し続ける。


実際、試した結果は上のようになるし、下のコードを試すと結果は false が出力される。

println("g==h => {g == h}");

クロージャのそれぞれの実体は異なるものになってる。これはコンパイラ次第なのかもしれない。スペックを読んでいないのでなんとも言えないし、(完全にオープンソースとなって登場すればの話だけど)実装次第なのかもしれない。


それからこんなことも。

But inner functions aren’t the only way that local variables can continue to exist after their enclosing function exits. The bind and trigger (”on replace”) language constructs can also extend the lifetime of local variables. More on that in my next post.

Local Variable Extent in JavaFX | Synchronous Messages

意味はこんな感じ。(しかし、そもそも訳の正確性には注意されたし!)
しかし、エンクロージング関数から出た後に、ローカル変数が存在し続けられる方法は、内部関数が唯一の方法ではない。"bind" と トリガー ("on replace") もまた、ローカル変数の生存期間を延長することができる。


なるほど。
"bind" とトリガー ("on replace") か。いかにも怪しい匂いがするよ。確かに。
ということで、次に続く。