getPropertyとsetProperty

getPropertyとsetProperty

Groovyでは、getPropertyという名前のメソッドをクラスに定義しておくと、プロパティから値を取得する際に呼ばれるようになります。また、setPropertyという名前のメソッドをクラスに定義しておくと、プロパティに値を設定する際に呼ばれるようになります。

[ソース]
class MyClass {
    def greeting = 'accessed greeting directly'
    def properties = [:]
    Object getProperty( String property ) {
        println "read from property $property"
        properties[ property ]
    }
    void setProperty( String property, Object newValue ) {
        println "wrote to property $property"
        properties[ property ] = newValue
    }
}
def mine = new MyClass( )
println mine.greeting
println '-----'
mine.greeting= 'accessed message indirectly'
println mine.greeting

[実行結果]
read from property greeting
null
        • -
wrote to property greeting read from property greeting accessed greeting indirectly

実行結果からもわかるように、getPropertyとsetPropertyメソッド定義すると、定義済のプロパティへのアクセスはできなくなってしまいます。また、どちらかだけ定義するということもできます。

[ソース]
class MyClass {
    def greeting = 'accessed greeting directly'
    def properties = [:]
    void setProperty( String property, Object newValue ) {
        println "wrote to property $property"
        properties[ property ] = newValue
    }
}
def mine = new MyClass( )
println mine.greeting
println '-----'
mine.greeting= 'accessed message indirectly'
println mine.greeting

[実行結果]
accessed greeting directly
        • -
wrote to property greeting accessed greeting directly

.@

invokeMethodを定義した際に、".&"で定義済のメソッドを直接呼ぶことができましたが、プロパティの場合は、「"オブジェクト" ".@" "プロパティ"」という形式で呼び出すことで、定義済のプロパティに直接アクセスすることができます。

[ソース]
class MyClass {
    def greeting = 'accessed greeting directly'
    def properties = [:]
    Object getProperty( String property ) {
        println "read from property $property"
        properties[ property ]
    }
    void setProperty( String property, Object newValue ) {
        println "wrote to property $property"
        properties[ property ] = newValue
    }
}
def mine = new MyClass( )
println mine.greeting
println '-----'
println mine.@greeting

[実行結果]
read from property greeting
null
        • -
accessed greeting directly

".@"の形式でプロパティにアクセスする場合、未定義のプロパティであれば、MissingFieldExceptionが発生します。

[ソース]
class MyClass {
    def greeting = 'accessed greeting directly'
    def properties = [:]
    Object getProperty( String property ) {
        println "read from property $property"
        properties[ property ]
    }
    void setProperty( String property, Object newValue ) {
        println "wrote to property $property"
        properties[ property ] = newValue
    }
}
def mine = new MyClass( )
println mine.greeting
println '-----'
println mine.message
println '-----'
println mine.@message

[実行結果]
read from property greeting
null
        • -
read from property message null
        • -
Exception thrown: groovy.lang.MissingFieldException: No such field: message for class: MyClass

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}

".&"と".@"の、文字列の変数展開によるアクセス

".&"による定義済メソッドへのアクセス、および".@"による定義済プロパティへのアクセスは、文字列の変数展開と組み合わせると、より柔軟なアクセスが可能になります。

[ソース]
class MyClass implements GroovyInterceptable {
    def greeting = 'accessed greeting'
    def id = 'White: '

    Object getProperty( String property ) {
        try{
            return this.@id + // プロパティへの直接的なアクセス
                   'indirectly ' +
                   this.@"$property" // プロパティへの直接的で動的なアクセス
        } catch( e ) {
            return "no such property $property"
        }
    }

    def hello( Object[] args ) {
        "invoked hello with (${args.join(', ')})"
    }

    def id( ) {
        'Green: '
    }

    def invokeMethod( String name, Object args ) {
        try{
            return this.&id( ) + // 直接的なメソッド呼び出し
                   'indirectly ' +
                   this.&"$name"( args ) // 直接的で動的なメソッド呼び出し
        } catch( e ) {
            return "no such method $name"
        }
    }
}

def mine = new MyClass( )
println mine.greeting
println mine.farewell
println mine.hello( 1, 'b', 3 )
println mine.foo( 'Mark', 19 )

[実行結果]
White: indirectly accessed greeting
no such property farewell
Green: indirectly invoked hello with (1, b, 3)
no such method foo