2009年7月1日水曜日

simple groovy code reading - closure

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を作るのに役立っていたりするのです...