Groovy の メタクラス でメソッドの処理を差し替えても Java から呼び出したら替ってないのか…

タイトルの通り。あくまでも メタクラス っていう Groovy 側が持っている物に登録しているから、それを利用せずに呼び出す Java からでは変更されないのかな?
Groovy イン・アクション の メタプログラミング の章 を読んでみようか。


ちなみに、やりたかった事はこんな感じ*1です。

DAC.java

interface IDAC { 
  Object getByPK(int id);
  void insert(Object dao);
}

public class DAC implements IDAC {
  public Object getByPK(int id) {
    // どっかのストレージ等から取得する
    // 今回はとりあえず null を返している
    return null;
  }
  public void insert(Object dao) {
    // どっかのストレージに登録する
    // 今回はとりあえず "insert" と出力する
    System.out.println("insert");
  }
}

Service.java

public class Service {
  private IDAC dac;
  public Service() {
    this.dac = new DAC();
  }

  public void proc() {
    while (true) {
      this.dac.insert(new Object());
      if (何かしら条件分岐()) {
        break;
      }
    }
  }

  private boolean 何かしら条件分岐() {
    // 何かしらの条件分岐に IDAC.getByPK が必要。
    return this.dac.getByPK(1) == null;
  }
}

ってな感じの Java のコードがあったとして、 Service.proc 内で IDAC.insert が何回呼ばれているかを調べるために、Groovy から呼び出して DAC の insert メソッド の実装を差し替えて呼び出されたカウントを取ろうとしました。

DAC.metaClass.insert = { Object dao -> (void) } // 型のメタクラスで insert メソッド を差し替える
def s = new Service();
def cnt = 0
s.dac.metaClass.insert = { Object dao -> // 更にインスタンスのメタクラスで insert メソッド を差し替える
  cnt++
  (void)
}
s.proc()
// s.dac.insert(new Object()) Groovy で呼び出した場合は、差し替えた処理が走り、cnt がインクリメントされる

assert s.dac.cnt == 1

試してみましたが、差し替えた処理は走らずに、元の処理("insert" と出力する) が走り cnt の値は 0 のままでした。
ちなみに Groovy から s.dac.insert メソッドを呼び出すと、差し替えた処理が走り cnt は増加していきました。


結局上の方法では実現出来ませんでしたが、Service.dac フィールド自体を差し替えると実現出来たので良しとします。

class DACCustom extends DAC {
  int cnt = 0
  // getByPK は変更しない
  public void insert(Object dao) {
    cnt++;
  }
}

def s = new Service();
s.dac = new DACCustom()
s.proc()

assert s.dac.cnt == 1

*1:サンプルなのでかなり簡略化したコードを提示しています。ほんとはよくわからない DIコンテナとかがあって DAC の実装クラスが無かったりします