倭マン's BLOG

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

() で call() メソッドが呼ばれないこともあるようで。

Closure 型のプロパティを使って

class Main{
    def closure = { println 'This is closure.' }
}

def main = new Main()
main.closure()

とやると普通に実行できて「This is closure.」と表示されます。 Groovy ではオブジェクトに対して () を付けると call() メソッドの呼び出しになるはずなので、以下のようなコードが動くかなぁと思ったんですが

class Main{
    def callable = new MyCallable()
}

class MyCallable{

    def call() {
        println 'This is Callable.'
    }
}

def main = new Main()
main.callable()

実際に実行してみると例外が投げられました(Groovy 2.0.1)。 callable() メソッドがないっていう例外のようです。 ちなみに、同じ Main クラス、MyCallable クラスを用いて

def main = new Main()

// 一旦、プロパティを変数に格納
def callable = main.callable
callable()    // 「This is Callable」と表示

// main.callable を先に評価させる
(main.callable)()    // 「This is Callable」と表示

とすると、思ったように表示されます。

まぁ、メソッドとプロパティに対する call() メソッドをキチンと使い分ければいいだけですが、敢えてあいまいな使い方をできるようにする方法を見ていきましょう。 大雑把には次の2つです(他にもあるかも知れませんが):

  • Main クラスの methodMissing() メソッドを実装する
  • 自作の Closure クラスを作製する

Main クラスの methodMissing() メソッドを実装する

まずは Groovy のメタプログラミングによる方法。 この方法には、Main クラス自体をいじるものと、実行時に MetaClass をいじるものがありますが、どちらも同じようなものです。

まずは Main クラスをいじる方法:

class Main{
    def callable = new MyCallable()

    // メソッドがない場合は、プロパティを取得して call() メソッドを呼び出す
    def methodMissing(String name, args){
        this."$name".call()
    }
}

class MyCallable{

    def call() {    // cannot be doCall()
        println 'This is Callable.'
    }
}

def main = new Main()
main.callable()    // 「This is Callable.」と表示

常に Main クラスのコードが書き換えられるとは限らないので、MetaClass を使って以下のように実行時に methodMissing() メソッドを追加する方が融通の利く方法かと思います:

class Main{
    def callable = new MyCallable()
}

class MyCallable{

    def call() {    // cannot be doCall()
        println 'This is Callable.'
    }
}

// Main クラスに methodMissing() メソッドを追加
Main.metaClass.methodMissing = { String name, args ->
    delegate."$name".call()
}

def main = new Main()
main.callable()    // 「This is Callable.」と表示

言うまでもないかも知れませんが、Main クラスをインスタンス化する前にメタクラスをいじりましょう(試してるときにちょっとハマったので)。

自作の Closure クラスを作製する


次は自作の Closure クラス(のサブクラス)を作製する方法。 一番、普通っぽいプログラミングって感じですが、Closure オブジェクトのインスタンス生成って重いのか軽いのか不安・・・

class Main{
    def myClosure = new MyClosure()
}

class MyClosure extends Closure{

    MyClosure() {
        super(null)
    }

    @Override
    def doCall() {    // cannot be call()
        println 'This is my closure.'
    }
}

def main = new Main()
main.myClosure()    // 「This is my closure.」と表示

Closure クラスのサブクラスでは doCall() メソッドをオーバライドする必要があります。 call() メソッドを実装していると、実行時に「doCall() メソッドがない」という例外が投げられます。

まぁ、これらの方法を多用するとコードのメンテナンスが大変になりそうだけど、メソッドの代わりに Closure 型のプロパティを定義するってのは Grails とか Griffon で普通に使われてるかと思うので、自分で定義したクラスについても出来るようになってほしいなぁ。

プログラミングGROOVY

プログラミングGROOVY


Groovyイン・アクション

Groovyイン・アクション