2004/3/25初稿、2004/7/22公開のため修正加筆
上原潤二(NTTソフトウェア)
1 イントロダクション 2 本文書の位置づけ 3 ひとめぐり 3.1 世界に対する挨拶 3.2 便利なコレクション 3.3 クロージャと内部イテレータ 4 特徴 4.1 実行に関するもの 4.2 言語機能に関するもの 4.3 ライブラリやフレームワーク 5 欠点 6 適用領域 6.1 テスト駆動開発アプローチ 6.2 ソフトウェアのコンフィグレーション 7 まとめ 7.1 スクリプト言語の隆盛が意味するもの 7.2 究極のEoD 7.3 Rubyについて 7.4 テスト駆動アプローチ 8 インストール方法 9 参考にしたリンク
GroovyはJavaVM上で動作するオブジェクト指向スクリプト言語です。 Groovyの文法はJavaをベースに大幅に簡潔に書けるようにした上、 同様のオブジェクト指向スクリプト言語であるRubyや C#を含むさまざまな言語の長所や工夫を取り込む形で 意欲的な拡張がなされています。 またGroovyではクラスの定義を含むJavaの全機能・全ライブラリを使うことができます。 このように大変高機能でありながらも、 常識的で整理されたシンタックスとセマンティクスを持ち、たいへん理解しやすいものです。 Groovyスクリプトは(Javaソースを経由せず)バイトコードに直接コンパイルされてJVM上で実行されます。 このようにJavaVMをインフラとして用いることにより、Groovyは多くのプラットフォームやツールや ライブラリにすでに対応していることになります。活発な開発が行われており、 まだまだ荒削りですがJSR 241として標準化されようとしており、注目すべき言語であるといえます。
GroovyはJames Strachan氏を中心に活発に開発が行われているオープンソースプロジェクトであり、 BSD/Apacheスタイルのライセンスで配布されています。
本文書を読むにあたっては7章以外についてはJavaの文法と実行に関する基本的な知識があることを 前提としています。7章のまとめについてはJavaの知識は不要です。
本文書の記述はGroovy beta6の動作を対象にしています。
例によってGroovyのHelloWorldを書いてみましょう。
簡単ですね。実行するには以下のようにします。hello.groovyprintln "Hello World"
> groovy hello.groovy Hello Worldこの段階でいくつかのことがわかります:
hello2.groovypublic class Hello2 { public static void main(String[] args) { java.lang.System.out.println("Hello World"); } }
これも立派な正しいgroovyのプログラムです。実行結果は以下のとおり。
> groovy hello2.groovy Hello WorldGroovyはさまざまな省略や暗黙変換を導入することで、 プログラムをとにかく楽に簡潔に書けるようにしています。 しかしその根幹にあるのはJavaの実行モデルそのままであり、 JVMであり、オブジェクト指向であり、 そこからいささかもはみ出すものではありません。 なお、以下のようにワンライナーを実行することもできます。(beta6以降)
> groovy -e 'println "Hello World"' Hello World
Groovyの簡潔性がもっとも発揮されるのはコレクションクラスの取り回しです。 以下のコードをご覧ください。
これを実行すると以下のようになります。list.groovymylist = [1, 2, 3] for (i in mylist) { println i }
> groovy list.groovy 1 2 3mylistはjava.util.ArrayListとして初期化されて実行されます。 これと等価なJavaのコードは例えば以下のとおりです。
ごらんのようにJavaにくらべて記述が非常に簡潔になります。 一説には、等価な機能を実現するためのgroovyとJavaのコーディング量の比率は1:3程度だといわれています。(上は特に差が顕著な例だとは思います。)。ListTest.javaimport java.util.*; public class ListTest { public static void main(String[] arg) { List mylist = new ArrayList(); mylist.add(new Integer(1)); mylist.add(new Integer(2)); mylist.add(new Integer(3)); for (Iterator it = mylist.iterator(); it.hasNext(); ) { Integer value = (Integer)it.next(); System.out.println(value.intValue()); } } }
a={ println "hello" }
このようにクロージャは変数に代入することができますし、
メソッドの引数に渡したり、
以下のように呼び出したりすることができます。
これを実行すると以下のようになります。closure.groovya={ println "hello" } a();
> groovy closure.groovy helloクロージャは引数を取ることができます。
ちなみにこのようにPerlやRubyのように文字列中に${と}でくくることで変数pの値を展開することができます。 なお${と}の間には変数だけでなく任意の式が書けます。実行結果は以下のとおり。closure2.groovya={| p | println "hello ${p}" } a("universe"); a("hoge");
> groovy closure2.groovy hello universe hello hogeクロージャはJavaでいう無名内部クラス(annonymous inner class)のようなものです。
each.groovymylist = [1, 2, 3] mylist.each { println it }
ここで「it」は暗黙の変数でPerlの$_のような存在です。itを使わずに
mylist = [1, 2, 3]
mylist.each {| p | println p }
と書くこともできます。
上をmylist変数を使わずに以下のように書くこともできます。
[1, 2, 3].each {
  println it
}
eachはリストの単なるメソッドであり引数としてクロージャをとります。 つまりここではループを制御するfor文のような制御構造は使われていないのです。
ちなみに以下のように [1,2,3]の代わりに「範囲」を生成する演算子「..」を使うことができます。
(1..3).each {
  println it
}
実行結果は以下のとおり。
1 2 3ピリオド2つの「..」の代わりに3つの「...」を使うこともできます。
(1...3).each {
  println it
}
すると結果は
1 2となります。要するに前者はfor (i=0; i<=3; i++)のループで後者は for (i=0; i<3; i++)だということです。
クロージャは繰り返しのためだけのものではなく、集合に対する処理を抽象化することにも使えます (GoFのパターンで言うVisitorの役割をはたす)。 この用途として、each以外にany、every, collect, find, findAllなどのメソッドでクロージャが便利につかえます。 例えば以下は整数のリスト[1,2,..100]から偶数だけのリストを取り出すコードです。
findall.groovyp = (1 .. 100).findAll {|x| return x % 2 == 0 } println p;
> groovy findall.groovy [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98, 100]
Integer i = null; println i.intValue(); // ここでNullPointerException println i->intValue(); // こちらでは例外は起きない(nullが表示される)
println "${i} ${a.b.c + d.e.f}";
println "abc def"; println<<<EOT this is here document EOT
「Groovy Markup」というのがあってこれは何かというと、 説明が難しいのですが、ツリー構造データを、Groovyのメソッド呼び出し構文とクロージャを使って組み立てるというものです。 ツリー構造データとは例えばDOMツリーであり、SwingのGUI部品の組み立てであり、あるいはANTのbuild.xmlの内容とかです。
XMLに関して言うとこれはプログラミング言語をしのぐほど複雑化していくXML表現 に対するアンチテーゼです。XMLの表現形式として(可読性低劣で冗長性過剰な)XMLでは なくプログラミング言語で書いたほうがましだ、ということです。
以下、SwingのGUIをGroovyMarkupを使って組み立てるSwingBuilderのサンプルです。
swing.groovybuilder = new groovy.swing.SwingBuilder() widget = builder.frame(title:'My Frame') { panel() { label(text:"label") textField(text:"text") button(text:'OK', actionPerformed:{ println("I've been clicked with event ${it}") }) } } widget.size = [100,100] widget.show()
これを実行すると以下のようなSwingのフレームが表示されます。
これはたとえば、Windowsアプリケーションの開発ではリソース定義言語でGUIの配置などを記述するわけですが、そのような特別なリソース記述言語ではなく、プログラミング言語そのもので書いていく、というようなことです。 Groovy Markupではその際に言語のフル機能(繰り返しや条件判断)を使うことができることがメリットになります。プログラム言語の表現力の拡張という意味で大変に興味深い機能です。
Groovyの役割はこの側面からはいくつかあって、
オブジェクト指向言語に限っても、厳しい型付けを行う言語と動的な型付けを行う言語では、 それぞれの支持者の間で長い激しい論争が繰り返し行われてきました。 方や、厳密な型は安全なプログラムを作るために重要だといい、 方やそうじゃなくてすばやく動作させてテストを網羅的に きちんとやるほうがいい、というようなことです。前者の代表はJavaやC++であり、 後者はSmalltalkやSqueak, Python、Ruby流ということになります。
さて、この論争はどういう結果になったでしょうか? 驚くべきことにこれらの相反するアプローチのどうやら両方が正しい、ということを 現実が示しているように思えます。 WebアプリケーションのCoolなサイトは多くはPythonやPHPなどのスクリプト言語で書かれていますし、 企業システムやサーバアプリケーションではJavaの優勢は揺らぎそうにありません。 言語の優劣をその普及度で計ることができないのかもしれませんが、 少なくともどちらかが衰退するということはまったく起きていないのです。
月並みな結論ですが、単一の言語ではだめで やはり適材適所なのだということです。 そして、GroovyはJavaの厳格な世界に動的アプローチを注入し、 両者の組み合わせで最高の生産性を与えようというものです。 ここで重要なのは、GroovyはPythonユーザやRubyユーザが作ったのではなく、 RubyやPythonのことがうらやましくてしょうがないJava開発者がJavaのためのみに作ったものであるということです。 Groovyの開発者や潜在ユーザからは 「これでRubyを見て悔しい思いをしなくてすむ」 という切実で身勝手な思いが伝わってきます。
近年、Sunの提唱するEoD(Ease of Development;開発を簡単にしよう)構想に基づき、 さまざまな拡張や提案がJavaの世界でなされてきています。例えば PHPとJavaを連携させる JSR 223であるとか、 JDK 1.5 "Tiger"でのJava言語仕様自体の一連の拡張とか、JSP ELとかです。
Groovyはこのような流れに沿うものですが、 これらと比べて最もラジカルで根本的なアプローチに特徴があるように思います。 どのぐらい根本的かというと いままで営々と構築・蓄積されてきたJava言語というものを構文レベルでいじりまくり、 躊躇無く改良しまくり、否定すらしているんではないかと感じられるほどの勢いです。
しかしこの改革はJava技術の表層である言語構文の側面のみに対するものであって、 GroovyはそのインフラであるJVMの技術を実に有効に活用しています。Javaの 価値とは何なのか、という問いに対してそれは表現ではなく実行モデルであり、 JVMであり、それらが提供するバイナリ互換性であり、プラットフォームニュート ラル性であり安全性だと、そういうことを提案している言語だといえます。
Rubyは好きな言語ですが業務で使用するのはちょっと条件が厳しいというか人を選ぶところがあります。 端的にはいざ開発プロジェクトを始めるときに「Rubyができるひと5人ぐらいそろえてください」 と人材派遣会社に要望を出すわけにはなかなか行かないということです。 その点、GroovyはJavaの知識がほぼそのまま生かせる、ということは利点です。
昔を思い起こせばJavaの登場当初、「C++がわかってればJavaは2日で理解できる」 ということが言われたことがありましたが、 膨大なAPIを除いた言語仕様に関して言えば確かにそんな気がしたものでした。 同じことがGroovyにもいえて、Javaがわかっていれば (将来日本語ドキュメント等が整備されることを前提に)Groovyは基本的なところはまあ数日でたぶん理解できるでしょう。
Rubyはいい言語ですが良きにつけ悪しきにつけ、
まつもとゆきひろ氏の美学に基づく「個人の言語」なのだと思います。
Groobyは言語設計においては独自の美学を持たず、
「既存言語からの言語機能の良いとこどり」に徹し、
「産業の言語」を目指しているように見えます。
8. インストール方法
本家GroovyのサイトのInstalling Groovyを参照のこと。
難しくないです。JDK1.4以降が必要です。
9. 参考にしたリンク