opamp_sando's blog

クソザコが割りと適当なことを書くためにある備忘録です。あとたまに普通の日記も書きます

Groovyで簡単な特異メソッド

Rubyでいう特異メソッドをGroovyで定義する時のメモ。
例えば、Rubyなら

obj1 = Object.new()
obj2 = Object.new()

def obj1.hello()
    puts "Hello world"
end

とするとobj1にだけhelloメソッドを持たせることができる。上のコードを実行した後irbなどでそれぞれhelloメソッドを呼びだそうとするとそれぞれ以下のようになる。

> obj1.hello()
Hello world
 => nil

> obj2.hello()
NoMethodError: undefined method `hello' for #<Object:0x007fc8d2199fb8>

同じObjectクラスからできてるがobj1では正しく実行され、obj2では上のようにメソッドが定義されていないとエラーになる。

こんな具合に"obj1"のように特定のオブジェクトに持たせるメソッドを特異メソッドとRubyでいうけど、今日はこれをGroovyでやってみる。

ここから本題


上のRubyの例と同じようなコードをGroovyで書くとこんな具合になるようだ。

obj1 = new Object()
obj2 = new Object()

obj1.metaClass.define {
    hello {println "Hello world"}
}
/*
後半3行は以下のように書いても良いみたい

obj1.metaClass.hello = {println "Hello world"}

*/

同様にgroovyshで実行してみると

> obj1.hello()
Hello world
===> nil

> obj2.hello()
ERROR groovy.lang.MissingMethodException:
No signature of method: java.lang.Object.hello() is applicable for argument types: () values: []
Possible solutions: sleep(long), sleep(long, groovy.lang.Closure), each(groovy.lang.Closure), getAt(java.lang.String), split(groovy.lang.Closure), wait()

という具合にobj2ではエラーが出る。ということでobj1にのみメソッドを定義することができた。


もう少し発展させてみる。

obj = new Object()

//引数を渡したい場合
obj.metaClass.hello = {s-> println "hello " + s}
//obj.hello("opamp") => "hello opamp" と表示される


//複数引数を渡したい場合
obj.metaClass.hello = {s,y-> println "Hello " + s + " " + y}
//obj.hello("opamp","sando") => "Hello opamp sando"と表示される


//複数のメソッドを同時に定義したい場合
obj.metaClass.define {
    hello {s-> println "Hello " + s}
    world {println "world"}
}
//これでobjにhelloとworldメソッドがそれぞれ追加される。

こんなもんかな?


ちなみにこんなふうにも使える

Object.metaClass.define = {
    hello {println "hello world"}
}

obj1 = new Object()
obj2 = new Object()

obj1.hello() // => hello world
obj2.hello() // => hello world
//こんな具合にObjectクラス自体に後付けでhelloメソッドを定義してやることもできる。


Groovy初心者だけじゃなくて、Rubyとかみたいな動的な言語もあんまり経験ないんでクロージャとか言われてもかなりaboutにしかわからなかったりするからこんな記事しかかけないorz

Firefox ブラウザ無料ダウンロード