倭マン's BLOG

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

動静同名メソッド

確認 その1


ちょっと Java クラスの確認。 1つの Java クラス(Groovy クラスも同じ)に、名前とシグニチャが同じインスタンス・メソッドと static メソッドを同時に定義するとコンパイル・エラーになります:

// Java
public class MyPojo {

    public static void say(){
        System.out.println("Hello, STATIC world!");
    }

    public void say(){
        System.out.println("Hello, INSTANCE world!");
    }
}

確認 その2


Java クラス(Groovy クラスも同じ)に static メソッドが定義されているとき

// Java
public class MyPojo {

    public static void say(){
        System.out.println("Hello, STATIC world!");
    }
}

この static メソッドは、クラスからもインスタンスからも呼び出せます(IDE によっては警告が出されますが):

// Groovy
MyPojo.say()
new MyPojo().say()

実行結果はこうなります:

Hello, STATIC world!
Hello, STATIC world!

動静同名メソッド


さて、ここで Groovy のメタ・クラスを使って、MyPojo クラスに Closure 型の say プロパティを定義して・・・

// Groovy
MyPojo.metaClass.say = { println 'Hello, DYNAMIC INSTANCE world!' }

MyPojo.say()
new MyPojo().say()

これを実行すると

Hello, STATIC world!
Hello, DYNAMIC INSTANCE world!

といった感じに、static メソッドとインスタンス・メソッドで挙動を変えることができるように見えまぁ〜す!*1 まさに、動静同名の異なるメソッド

Groovy のメタ・プログラミングに精通している人には当たり前の挙動なのかも知れませんが、何か得した気分(笑) ただ、実際に使うとコードの保守が無駄に複雑になるだけのような気もするけど。

注意 その1

static メソッドとインスタンス・メソッドを逆にすると、同じようには動作しません。 例えば、以下のような Java クラス

// Java
public class MyPojo2 {

    public void say(){
        System.out.println("Hello, INSTANCE world!");
    }
}

に対して

// Groovy
MyPojo2.metaClass.'static'.say = { println 'Hello, DYNAMIC STATIC world!' }

MyPojo2.say()
new MyPojo2().say()

のようなコードを実行すると

Hello, DYNAMIC STATIC world!
Hello, DYNAMIC STATIC world!

という風に、メタ・クラスで定義したメソッド(Closure 型のプロパティ)が実行されます。

注意 その2

動静同名メソッド(MyPojo クラスの場合*2)は Groovy コード中で呼び出すと異なる動作をしますが、Java コード中で呼び出すと同じ動作(static メソッド呼び出し)をします。 例えば以下のような Groovy クラス

// Groovy
class SayPropertyInjector {

    def static inject(){
        MyPojo.metaClass.say = { println 'Hello, DYNAMIC INSTANCE world!' }
    }
}

を使って say プロパティを定義し、Java コード中で say() メソッドを呼び出すと

// Java
public class MyPojoMain {

    public static void main(String... args){
        SayPropertyInjector.inject();

        MyPojo.say();
        new MyPojo().say();
    }
}

実行結果は

Hello, STATIC world!
Hello, STATIC world!

となり、もともとの Java クラスに定義されている say() メソッドが呼ばれます(Groovy クラスでも同じ)。

Groovyイン・アクション

Groovyイン・アクション

*1:実際には Closure 型の say プロパティを取得して、それを実行してるんですけど。

*2:MyPojo2 クラスの場合は下記のようなサンプルコードはコンパイル・エラーになる。