Groovy の MetaClass でメソッドをオーバーライドする時にはまったこと
Groovy の便利な仕様として、メタプログラミングがあります。
クラスやメソッドを呼び出すときに、そのクラスの定義を変更して振る舞いを変えることができます。
その実装方法はシンプルでわかりやすいものとなっています。
ですが、はまりました
シンプルなコードで MetaClass を試してみたところ簡単に実装できました。
これは使えるとばかりに仕事で使ってみたところ、うまくメソッドがオーバーライドされなくて悩みました。試行錯誤してオーバーライドされない原因がわかりました。
型指定したパラメータをもつメソッドをオーバーライドするときは、Closure のパラメータに型指定しないといけません。
以下、サンプルコードです。
まずはオーバーライドする対象のクラスです。
class Sample { String execute(String val) { val } }
次に、オーバーライド失敗しているテストケースと成功しているテストケースです。
class MetaProgramingTest extends spock.lang.Specification { def "引数ありメソッドをメタプログラミングでオーバーライドする - 失敗パターン"() { given: JavaSample sample = new JavaSample() sample.metaClass.execute = { val -> "2" } when: def val = sample.executeParam("1") then: val == "1" } def "引数ありメソッドをメタプログラミングでオーバーライドする - 成功パターン"() { given: JavaSample sample = new JavaSample() sample.metaClass.execute = { String val -> "2" } when: def val = sample.executeParam("1") then: val == "2" } }
よくよく考えてみたら、型指定しないとオーバーロードしているメソッドがあった場合、どのメソッドをオーバーライドするか判断できないですよね。
それが本当の理由かわかりませんが、たぶん理由のひとつでしょう。
Groovy 初級的な文脈でよく MetaClass が紹介されているのですが、この「引数ありメソッドのオーバーライド」ってあんまり詳しく言及されていないと思います。
Groovy を使っていると状況に応じて型指定したり、しなかったりします。メタプログラミングのときは気をつけないといけませんね。
ちなみに
次のようにオーバーライド対象のメソッドの引数の型指定しない場合、上記どちらのテストケースでもオーバーライドできます。
class Sample { String execute(val) { val } }
動的型付けに対する理解が少し深まった気がします。