GeekFactory

int128.hatenablog.com

Groovy 2.3および2.4におけるTraitの差異

Groovy SSHの開発で気づいたのですが、Groovy 2.3と2.4でTraitの挙動が微妙に異なるようです。

2.3から2.4に上げたところ、Traitのメソッドに書いたクロージャdelegate指定で実行してもdelegateのプロパティやメソッドが解決できない問題が発生しました。例えば、以下のコードの実行結果は2.3と2.4で異なります。

// クロージャをdelegate指定で実行する
class U {
  static <T> T callWithDelegate(Closure<T> closure, Object delegate) {
    def cloned = closure.clone() as Closure<T>
    cloned.resolveStrategy = Closure.DELEGATE_FIRST
    cloned.delegate = delegate
    cloned.call()
  }
}

class A { final a = 100 }

trait X {
  def x() { U.callWithDelegate({ -> println a }, new A()) }
}

class Y implements X {
  def y() { x() }
}

new Y().y()

Groovy 2.3.10では期待通りの値が返されます。

100

Groovy 2.4.4ではMissingPropertyExceptionが発生し、delegateのプロパティが解決できないことが分かります。

Caught: groovy.lang.MissingPropertyException: No such property: a for class: Y
groovy.lang.MissingPropertyException: No such property: a for class: Y
    at X$Trait$Helper$_x_closure1.doCall(script.groovy:13)
    at U.callWithDelegate(script.groovy:6)
    at U$callWithDelegate.call(Unknown Source)
    at X$Trait$Helper.x(script.groovy:13)
    at X$Trait$Helper$x.call(Unknown Source)
    at Y.x(script.groovy)
    at Y.y(script.groovy:17)
    at Y$y.call(Unknown Source)
    at script.run(script.groovy:20)

2.4.4のバグなのか2.4系の仕様なのか分かりませんが、Traitを利用している部分はテストを書いて挙動を確認できるようにしておいた方がよさそうです。

ちなみに、2.4で改善されたものもありました。クラスに @Slf4j を付けても、2.3では log プロパティにアクセスできませんでしたが、2.4からはアクセスできるようになっています。