動的メソッド追加の謎
[GROOVY-3674] In Closure as injected method of Closure, delegate is not collect - jira.codehaus.orgを見て、少し実験してみました。
Javaのクラスの場合、最初はメタクラスがないからグローバルなやつを使うんだよ、との事で。つまりメタクラス拡張を行った後だとStringでも例外が発生する?ぽい感じです。
で、やってみると…
String str = 'A' String.metaClass.test = { assert delegate instanceof String assert delegate == str } str.test()
は動きますが、
String str = 'A' str.metaClass.getFoo = {return "foo"} String.metaClass.test = { assert delegate instanceof String assert delegate == str } str.test()
とすると
Exception thrown: No signature of method: java.lang.String.test() is applicable for argument types: () values: [] groovy.lang.MissingMethodException: No signature of method: java.lang.String.test() is applicable for argument types: () values: [] at ConsoleScript0.run(ConsoleScript0:10)
のように例外が発生します。ここまでは書いてある通りです。なるほどねー。
…ただし、まず1個目を実行した後GroovyConsoleを開いたままで2個目を実行すると例外が起きません!
さらに、2回目で
String str = 'B' str.metaClass.getFoo = {return "foo"} String.metaClass.test = { assert delegate instanceof String assert delegate == str } str.test()
とstrの値を変えて実行すると…
Exception thrown: Expression: (delegate == str). Values: delegate = B, str = A java.lang.AssertionError: Expression: (delegate == str). Values: delegate = B, str = A at ConsoleScript1$_run_closure2.doCall(ConsoleScript1:7) at ConsoleScript1$_run_closure2.doCall(ConsoleScript1) at ConsoleScript2.run(ConsoleScript2:10)
なんと!delegateが呼び出し対象のオブジェクトとは違うオブジェクトを指しているではありませんか!
なんともトリッキーな…てか、これは仕様バグと言って良いのではないでしょうか。
まぁ、Javaのオブジェクトにメタクラス拡張をかけるのは非常に危険だ、という事ですね。おそろしや…