trait内のクロージャからプライベートメソッドが見えない
Groovy 2.3から導入されたtraitを使っていて、妙な事象に遭遇したのでまとめてみます。具体的には、traitの中でクロージャを使う場合に、クロージャからプライベートメソッドが見えない仕様があるようです。
例えば、下記のような trait T
があるとします。
trait T { private a(x) { "--$x--" } // プライベートメソッドを直接呼んでいる def b(x) { a(x) } // クロージャの中からプライベートメソッドを呼んでいる def c(xs) { xs.collect { x -> a(x) } } }
下記のように、この trait を適用した class P
および class Q
を定義します。
// implementsでtraitを適用する class P implements T {} // withTraitsでtraitを適用する class Q {}
P, Q に対してそれぞれメソッド b, c を呼び出します。メソッド b, c が期待通りに動くか検証してみます。
def p = new P() println p.b('hoge') println p.c(['hoge'])
def q = new Q().withTraits T println q.b('hoge') println q.c(['hoge'])
Groovy 2.3.6の仕様
実行結果は下記のようになりました。withTraits
で適用すると、trait 内のメソッドでクロージャを使うと、プライベートメソッドが見えないことが分かります。
--hoge-- [--hoge--]
--hoge-- groovy.lang.MissingMethodException: No signature of method: Q1_groovyProxy.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
Groovy 2.4.0の仕様
実行結果は下記のようになりました。いずれの適用方法でも、trait 内のクロージャからプライベートメソッドが見えないことが分かります。見えない仕様に統一されたのかもしれません。
--hoge-- groovy.lang.MissingMethodException: No signature of method: P.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
--hoge-- Caught: groovy.lang.MissingMethodException: No signature of method: Q1_groovyProxy.a() is applicable for argument types: (java.lang.String) values: [hoge] Possible solutions: b(java.lang.Object), c(java.lang.Object), any(), is(java.lang.Object), any(groovy.lang.Closure), wait()
詳しい情報をお持ちでしたらぜひ教えてください。traitで拡張可能なDSLを作ろうとするとこの仕様はつらいですね。