Groovy最大の特徴のひとつであるクロージャは, 謎でも何でもなく単なるClosureクラスのインスタンスです. なので, クロージャが何かはgroovy.lang.Closureを見てみるのがいちばんです.
Closureでいちばん目立つのは, call()とcurry()です. call()は名前のまま, そのクロージャに引数を与えて実行します. 単にmetaClassのinvokeMethodを呼んでいるだけですね.
curry()は引数の一部を固定したクロージャを作るものですが, その通り, 引数の一部を値として持つサブクラスCurriedClosureを作って返しているだけです. 実際に呼び出すときには, この固定した値をもとのClosureの引数として与えなければならないのですが, それはinvokeMethodを実装したMetaClassImplの中で行われています.
その他にも妙なものがありますよ. resolveStrategyですね. 値は
- DELEGATE_FIRST
- DELEGATE_ONLY
- OWNER_FIRST
- OWNER_ONLY
- TO_SELF
の5種類です.
Closureはownerとdelegateの二つのプロパティを持っています. ownerはそのClosureを定義したオブジェクトです. delegateは, クロージャが自分で解決できないプロパティを参照したときに, resolveStrategyに従って探しに行くオブジェクトです. デフォルトではdelegateはownerと同じであり, resolveStrategyはOWNER_FIRSTです. したがって
class Foo { def a = 1; def aa = { println a}; def bb = { println b } }
foo = new Foo()
とすると foo().aa() は 1を出力しますが, 当然 foo().bb() はエラーになります.
ownerは変えることができませんが, delegateは変えることができますから,
class Bar { def a = 100; def b = 2 }
foo.bb.delegate = new Bar()
foo.aa.delegate = new Bar()
とすると
foo.bb()はdelegateのBarからbを探して2を出力しますが, foo.aa() は 1のままです.
ここで
foo.aa.resolveStrategy = Closure.DELEGATE_FIRST
すると, foo.aa()も先にBarからaを見つけて100を出力するようになります.
さて, このdelegateが何に役立つかというと, DSLを定義するのに便利なBuilderを作るのに役立っていたりするのです...