倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

果たしてそれが(どれが)委譲されるのか? :@Delegate アノテーション

@Delegate アノテーションをチョット使ってみて思ったことをメモ。

普通の使い方

まずは普通の使い方。 Wrapper1 クラスに Inner1 型のプロパティ inner を定義して、それに処理を委譲したいときは以下のように書きます:

class Wrapper1{

    @Delegate Inner1 inner

    Wrapper1(){
        this.inner = new Inner1(prop1:'1st Property')
    }
}

class Inner1{
    def prop1
}

このとき、Wrapper1 オブジェクトに prop1 プロパティがあるようにコードを書けます:

def var1 = new Wrapper1()
assert var1.prop1 == '1st Property'

まぁ、なんて便利なんでしょう。

Apparent or Actual ?


さて、次のような Inner1 クラスのサブクラス Inner2 があったとしましょう:

class Inner2 extends Inner1{
    def prop2
}

そして Wrapper2 クラスが以下のように定義されているとします:

class Wrapper2{

    @Delegate Inner1 inner

    Wrapper2(){
        this.inner = new Inner2(prop1:'1st Property', prop2:'2nd Property')
    }
}

このとき、先ほどと同じように

def var2 = new Wrapper2()
assert var2.prop1 == '1st Property'
assert var2.prop2 == '2nd Property'

としたらどうなるでしょう? 一見なんの問題もないように見えるかも知れませんが(拙者だけ?)、実際には prop2 プロパティにアクセスしようとした時点(3行目)で groovy.lang.MissingPropertyException が投げられます。

この原因は、Wrapper2 のプロパティとして、inner を Inner1 型で宣言しているためです。 この振る舞いから察するに、@Delegate はプロパティの本当の型 (Actual type) ではなく、表面的な型 (Apparent type) に依存するようです。

では、

class Wrapper2{

    @Delegate Inner2 inner

    Wrapper2(){
        this.inner = new Inner2(prop1:'1st Property', prop2:'2nd Property')
    }
}

とすれば、問題なく

def var2 = new Wrapper2()
assert var2.prop1 == '1st Property'
assert var2.prop2 == '2nd Property'

が実行できるかというと、今度は prop1 プロパティへアクセスする際(2行目)に groovy.lang.MissingPropertyException が投げられます。 正直、これはバグではないかと。 もしそうなら、先ほどの「表面的な型に依存する」ってのもそうかもしれませんが。

さらに Generics も混ぜてみた


もういいよ・・・って思われるかも知れないけど、更に Generics も混ぜて試してみた。

まず、次のようなクラスを作ってみる:

class GenericWrapper<I extends Inner1>{

    @Delegate I inner

    GenericWrapper(I inner){
        this.inner = inner
    }
}

で、試すのは次のようなコード:

def i1 = new Inner1(prop1:'1st Property')
def i2 = new Inner2(prop1:'1st Property', prop2:'2nd Property')

def var3 = new GenericWrapper<Inner1>(i1)
assert var3.prop1 == '1st Property'

def var4 = new GenericWrapper<Inner1>(i2)
assert var4.prop1 == '1st Property'
assert var4.prop2 == '2nd Property'

def var5 = new GenericWrapper<Inner2>(i2)
assert var5.prop1 == '1st Property'
assert var5.prop2 == '2nd Property'

さて、どのプロパティ・アクセスが失敗するでしょう?(1つとは限らない)

まず、上記のサブクラスの例からすると3つ目のアサーション中の「var4.prop2」が失敗する(例外が投げられる)と予想されるますが、実際失敗します。 んで、もうひとつ、5つめのアサーション中の「var5.prop2」も失敗します(例外が投げられる)。

まぁ、この辺りになってくると、仕様とかバグとか関係なく、使わない方が無難そうですけど。

Groovyイン・アクション

Groovyイン・アクション


Programming Groovy: Dynamic Productivity for the Java Developer (The Pragmatic Programmers)

Programming Groovy: Dynamic Productivity for the Java Developer (The Pragmatic Programmers)