2009年7月1日水曜日

simple groovy code reading - Object methods

Javaでは, Objectに定義されているメソッドは, ごく少数で基本的なものに限られています.



  • clone


  • eqauls


  • finalize


  • getClass


  • hashCode


  • notify, notifyAll


  • toString


  • wait


それに対して, Groovyでは, お馴染みprintln始め, 非常に多くのメソッドを使うことができます.


Javaでは次のように, きわめてトップダウンかつアドホックな思考パタンになっているわけです.


「printlnの責務を持つのは当然出力を担当するやつ (PrintStream) だよな. printlnしたければPrintStreamを出力先を指定してnewして, それにprintlnすればよかろう」


「ま, しかしprintlnなんてコードのどこからでもしたいだろうし, 出力先はほとんどは標準出力だろうから, ちょっとだけ便利にしてやろう. 標準出力先としてoutというインスタンスを用意してやろう」


「しかも特定のインスタンスにどこからでもアクセスするためのJavaのやり方はstaticメンバにするのが当たり前だから, そのためにSystemというクラスも作っといてやろう」


そのお陰で我々は"System.out."というあまり意味のない枕詞をそこら中に書かされる破目になっています.

じゃ, Groovyではどう考えるか.

「あるオブジェクトの中でやりたいことがある. としたら, それはそのオブジェクトに任せるのが筋だ. どんなオブジェクトでもやりそうなことならば, それはObjectに書いておけばよい. もちろん実装では, 最終的に出力を担当するやつにやらせることになるだろうが, それはここに書くべきことじゃない. それに'System'なんて訳分からんわ」


と言うわけで, GroovyではprintlnはObjectに実装されているのです.


では, Objectには他にどんなメソッドが追加されているか, 見てみることにしましょう.


それがorg.codehaus.groovy.runtime.DefaultGroovyMethodsです. ここにはおよそ700個以上のメソッドが定義されています. ただし, これらのメソッドの第一引数の型が追加対象の型を表しますから, すべてがObjectのメソッドになるわけではありません. Javaの既存の型 (コレクションやString, Streamなど) に追加したいメソッドがここに集結しています.


中でもObjectに追加される整理して挙げておきます.



  • foo.is(bar) - Javaでいう foo == bar


  • foo.with { a = 1; b = 2 } - 自分をdelegateとしてクロージャを呼ぶ


  • getAt(), putAt() - オブジェクトをあたかもMapのように扱う仕組み. 例えば foo[a] として, プロパティaにアクセスできます


  • dump() - オブジェクト情報を書き出します. toString()を一生懸命書かなくてもいいかも知れません. "foo".dump() ===> <java.lang.String@18cc6 value=foo offset=0 count=3 hash=101574>


  • metaPropertyValues - これは直接はあまり使わない. 次のgetPeopertiesを使う


  • foo.properties - プロパティとその値の一覧. "foo".properties ===> {class=class java.lang.String, bytes=[B@2e2167}


  • use - ご存知カテゴリ


  • mixin - Foo.mixin(Bar) とするとBarのメソッドをFooに追加します. コードを見ると, Foo.mixin([Bar, Baz]) とかできるのが分かります


  • addShutdownHook {...} - 名前のとおり


  • print系


  • invokeMethod("foo", 1) - this.foo(1)


  • isCase(bar) - ご存知switch/caseで使われるものですね


  • コレクション系 - find, eachなどコレクションで使われるメソッド群


  • metaClass - メタクラスへのアクセス


  • 型変換系


なかなか面白いでしょ?


不思議なのはコレクション系ですね. 例えばListにeachがあるのは当然ですが ([1,2,3].each { println it } ), なぜObjectにまでeachがあるのでしょう?


その理由は「普通の単一のオブジェクトもひとつの要素からなるコレクション (singleton) として扱いたい」からです. だってそうなれば, 普通のオブジェクトとコレクションを区別しなくて済みますよね. Listを返すべきところで, 要素がひとつしか無ければ, JavaではそれをListで包んで返す訳ですが, Groovyならばそのまま返してもいい, ということです (そういうのを嫌がる人も多いとは思いますけどね:-)


このruntimeパッケージには他にも面白いクラスがいっぱいあります.