GeekFactory

int128.hatenablog.com

Groovyのcall()をプロパティに対して適用するには?

TLDR

Groovyの call() はローカル変数に対しては機能するが、クラスのプロパティに対しては機能しない。解決法をご存じでしたら教えてください。

やりたいこと

変数に関数呼び出し演算子を適用した場合の処理を書きたいことがあります。例えば、下記のようにクロージャでプロパティを設定できたら便利です。

def x = new X()
x {
  value = 100
}

そんな時は当該クラスに call() メソッドを定義します。

class X {
  def value = 50
  def call(Closure c) {
    def cloned = closure.clone() as Closure
    cloned.resolveStrategy = Closure.DELEGATE_FIRST
    cloned.delegate = this
    cloned.call()
  }
}

def x = new X()
assert x.value == 50

// やりたいこと
x {
  value = 100
}
assert x.value == 100

しかし、このインスタンスを他クラスのプロパティにするとうまくいきません。

class Y {
  def x = new X()
}

def y = new Y()
y.x {
  value = 1000
}
assert y.x.value == 1000

そんなメソッドはないというエラーが発生します。

groovy.lang.MissingMethodException: No signature of method: Y.x() is applicable for argument types: (Script1$_run_closure2) values: [Script1$_run_closure2@69bfa8]
Possible solutions: is(java.lang.Object), any(), getX(), use([Ljava.lang.Object;), any(groovy.lang.Closure), wait()
    at Script1.run(Script1.groovy:26)

とりあえず、プロパティと同名のメソッドを定義すれば解決します。

class Y {
  def x = new X()
  def x(Closure closure) {
    //...
  }
}

しかし、すべてのプロパティに対して同名のメソッドを書くのは冗長です。何かいい方法をご存じでしたら教えてください。