invokeMethodとGroovyInterceptable

invokeMethod

Groovyでは、invokeMethodという名前のメソッドをクラスに定義しておくと、未定義のメソッドが呼ばれた際に呼ばれるようになります。

[ソース]
class MyClass {
    def hello( ) {
        'invoked hello directly'
    }
    def invokeMethod( String name, Object args ) {
        return "unknown method $name(${args.join(', ')})"
    }
}
def mine = new MyClass( )
println mine.hello( )
println mine.foo( "Mark", 19 )

[実行結果]
invoked hello directly
unknown method foo(Mark, 19)

上記の実行結果からもわかるとおり、定義済のメソッド呼び出しに対しては、invokeMethodが呼ばれることはありません。

GroovyInterceptable

GroovyInterceptableインタフェースを実装することにより、定義・未定義にかかわらず、すべてのメソッド呼び出しに対して、invokeMethodが呼ばれるようになります。

[ソース]
class MyClass implements GroovyInterceptable {
    def hello( ) {
        'invoked hello directly'
    }
    def invokeMethod( String name, Object args ) {
        return "unknown method $name(${args.join(', ')})"
    }
}
def mine = new MyClass( )
println mine.hello( )
println mine.foo( "Mark", 19 )

[実行結果]
unknown method hello()
unknown method foo(Mark, 19)

上記の実行結果からもわかるとおり、定義・未定義にかかわらず、常にinvokeMethodが呼ばれてしまいます。

.&

GroovyInterceptableインタフェースを実装した際、メソッド呼び出し時に、「"オブジェクト" "." "メソッド"」という形式ではなく、「"オブジェクト" ".&" "メソッド"」という形式で呼び出すことで、定義済のメソッドを呼び出すことができます。

[ソース]
class MyClass implements GroovyInterceptable {
    def hello( ) {
        'invoked hello directly'
    }
    def invokeMethod( String name, Object args ) {
        return "unknown method $name(${args.join(', ')})"
    }
}
def mine = new MyClass( )
println mine.&hello( )
println mine.foo( "Mark", 19 )

[実行結果]
invoked hello directly
unknown method foo(Mark, 19)

ただし、未定義のメソッドを".&"の形式で呼び出すと、上のソース例ではMissingMethodExceptionが発生します。

[ソース]
class MyClass implements GroovyInterceptable {
    def hello( ) {
        'invoked hello directly'
    }
    def invokeMethod( String name, Object args ) {
        return "unknown method $name(${args.join(', ')})"
    }
}
def mine = new MyClass( )
println mine.&hello( )
println mine.&foo( "Mark", 19 )

[実行結果]
invoked hello directly
Exception thrown: groovy.lang.MissingMethodException:
No signature of method: MyClass.foo() is applicable for argument types:
(java.lang.String, java.lang.Integer) values: {"Mark", 19}