Groovy 1.6 のアノテーション @Mixin と @Delegate

Groovy 1.6 の新機能である @Mixin と @Delegate アノテーションを Groovy 1.6 beta2 で試してみた。

@Mixin と @Delegate が実現する機能は以下の通り。

  • @Mixin は実装を取り込む(自分に機能を取り込む)
  • @Delegate は委譲処理を実現(自分に機能を取り込まずに他のオブジェクトに処理を委譲)

なお、1.6 beta2 には以下のようなアノテーションも用意されている模様。

  • @Singleton
  • @Immutable
  • @Delegate
  • @Lazy
  • @Category/@Mixin
  • @Newify

@Mixin と @Delegate のサンプル作成

@Mixin と @Delegate を使った以下のようなサンプルを作成。

以下では、SampleFeature クラスを @Mixin アノテーションで取り込んだ MixinSample クラスと @Delegate アノテーションを付与した feature に処理を委譲する DelegateSample クラスに同じメソッドを実行させている。

sample.groovy
class SampleFeature {
    def name

    def test(msg) {
        println "${this} : $name - $msg"
    }
}

//Mixin で SampleFeature の実装を取り込み
@Mixin(SampleFeature)
class MixinSample {
    void printName() {
        println name
    }
}

//@Delegate アノテーションを付与した feature に処理を委譲
class DelegateSample {
    @Delegate SampleFeature feature = new SampleFeature()

    void printName() {
        println name
    }
}

ms = new MixinSample(name: "Mixinテスト")
ms.printName()
ms.test "call test"

//setName が用意されないため以下は不可
//ds = new DelegateSample(name: "Delegateテスト")
ds = new DelegateSample()
//以下も不可
//ds.name = "Delegateテスト";
ds.feature.name = "Delegateテスト"
ds.printName()
ds.test "call test"

仕様かどうかは判断つかないが、@Delegate を使った場合には DelegateSample オブジェクトの name に値を設定する事はできない。(そのため、feature の name に値を設定している)

実行結果
>groovy sample.groovy
Mixinテスト
SampleFeature@1402d5a : Mixinテスト - call test
Delegateテスト
SampleFeature@18c3679 : Delegateテスト - call test

test メソッドの実行に関しては、どちらも SampleFeature インスタンスのメソッド呼び出しを実行しているようだ。


ここで、DelegateSample で直接 name に値を設定する事ができない原因を調査するため、以下のような処理を追加し、メソッドの一覧を表示してみることにした。

def printMethods = {
    println "--- ${it} methods ---"
    it.metaClass.methods.each {m -> println m}
}

printMethods MixinSample
printMethods DelegateSample

結果は以下の通りで、DelegateSample クラスには setName が実装されていない事が判明。さらには @Mixin と @Delegate では実装の仕方が全く異なる事もわかった。

メソッド一覧出力結果(加工済み)
--- class MixinSample methods ---
・・・
org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod@1e2befa[name: getName params: {} returns: class java.lang.Object owner: class MixinSample]
org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod@18f51f[name: setName params: {class java.lang.Object} returns: void owner: class MixinSample]
org.codehaus.groovy.runtime.metaclass.MixinInstanceMetaMethod@b30913[name: test params: {class java.lang.Object} returns: class java.lang.Object owner: class MixinSample]
public void MixinSample.printName()
・・・

--- class DelegateSample methods ---
・・・
public java.lang.Object DelegateSample.getName()
public java.lang.Object DelegateSample.test(java.lang.Object)
public void DelegateSample.printName()
public SampleFeature DelegateSample.getFeature()
public void DelegateSample.setFeature(SampleFeature)
・・・