Deprecated: The each() function is deprecated. This message will be suppressed on further calls in /home/zhenxiangba/zhenxiangba.com/public_html/phproxy-improved-master/index.php on line 456
オブジェクト指向スクリプト言語Groovy
[go: Go Back, main page]

オブジェクト指向スクリプト言語Groovyの紹介

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 参考にしたリンク

1. イントロダクション

GroovyはJavaVM上で動作するオブジェクト指向スクリプト言語です。 Groovyの文法はJavaをベースに大幅に簡潔に書けるようにした上、 同様のオブジェクト指向スクリプト言語であるRubyや C#を含むさまざまな言語の長所や工夫を取り込む形で 意欲的な拡張がなされています。 またGroovyではクラスの定義を含むJavaの全機能・全ライブラリを使うことができます。 このように大変高機能でありながらも、 常識的で整理されたシンタックスとセマンティクスを持ち、たいへん理解しやすいものです。 Groovyスクリプトは(Javaソースを経由せず)バイトコードに直接コンパイルされてJVM上で実行されます。 このようにJavaVMをインフラとして用いることにより、Groovyは多くのプラットフォームやツールや ライブラリにすでに対応していることになります。活発な開発が行われており、 まだまだ荒削りですがJSR 241として標準化されようとしており、注目すべき言語であるといえます。

GroovyはJames Strachan氏を中心に活発に開発が行われているオープンソースプロジェクトであり、 BSD/Apacheスタイルのライセンスで配布されています。

2. 本文書の位置づけ

本文書は筆者がドキュメントを眺めた上でとりいそぎ作成したものです。もしお時間があれば じっくりとGroovyのページを読むのがよろしいでしょう。 本文書は上のページにある情報の要約と、ちょっと試した上での感想以上のものではありません。 ただ、上のサイトに行って英語を読むのが面倒だという向きには、 本文書にざっと目を通すことは有益でしょう。

本文書を読むにあたっては7章以外についてはJavaの文法と実行に関する基本的な知識があることを 前提としています。7章のまとめについてはJavaの知識は不要です。

本文書の記述はGroovy beta6の動作を対象にしています。

3. ひとめぐり

ここでは、いくつかのサンプルコードとその実行結果を見ることを通じて簡単にGroovy言語を紹介します。

3.1 世界に対する挨拶

例によってGroovyのHelloWorldを書いてみましょう。

hello.groovy
println "Hello World"
簡単ですね。実行するには以下のようにします。
> groovy hello.groovy
Hello World
この段階でいくつかのことがわかります: さて、これらの省略をしないで以下のようにも書くこともできます。
hello2.groovy
public class Hello2 { public static void main(String[] args) { java.lang.System.out.println("Hello World"); } }

これも立派な正しいgroovyのプログラムです。実行結果は以下のとおり。

> groovy hello2.groovy
Hello World
Groovyはさまざまな省略や暗黙変換を導入することで、 プログラムをとにかく楽に簡潔に書けるようにしています。 しかしその根幹にあるのはJavaの実行モデルそのままであり、 JVMであり、オブジェクト指向であり、 そこからいささかもはみ出すものではありません。

なお、以下のようにワンライナーを実行することもできます。(beta6以降)
> groovy -e 'println "Hello World"'
Hello World

3.2 便利なコレクション

Groovyの簡潔性がもっとも発揮されるのはコレクションクラスの取り回しです。 以下のコードをご覧ください。

list.groovy
mylist = [1, 2, 3] for (i in mylist) { println i }
これを実行すると以下のようになります。
> groovy list.groovy
1
2
3
mylistはjava.util.ArrayListとして初期化されて実行されます。 これと等価なJavaのコードは例えば以下のとおりです。
ListTest.java
import 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()); } } }
ごらんのようにJavaにくらべて記述が非常に簡潔になります。 一説には、等価な機能を実現するためのgroovyとJavaのコーディング量の比率は1:3程度だといわれています。(上は特に差が顕著な例だとは思います。)。
Javaの抽象度は高く、高機能な大規模標準ライブラリも整備されているのでC(やC++)と比べるとコーディング量は数分の1になると言われていますが、Groovyはさらにその数分の1だということになります。
ここで差が生じている理由は以下のとおりです:
  1. GroovyにはArrayListのインスタンス生成・初期化のための構文がある('['と']'で要素をカンマで区切って並べる)が、Javaでは一連のメソッドを呼んで初期化しなければならない。
  2. JavaではArrayListへのint値の格納と取り出しでラッパクラスIntegerとの相互変換を行う必要がある。 Groovyではこの変換は暗黙に行われる(boxing, unboxing変換)。
  3. Iteratorをとりまわす簡潔なfor構文がある。
  4. Groovyでは変数の宣言が不要。
いわゆるスクリプト言語の特徴をもっているということです。
なお、2と3はJavaにおいてもJava 2 SDK 5.0("Tiger")で改善されました。

3.3 クロージャと内部イテレータ

JavaにはないGroovyの機能として大きなものにクロージャがあります。 クロージャは実行コードの断片であって、 データと同様に扱えるもの(一級市民オブジェクト)です。 Groovyではクロージャは'{'と'}'でくくることで作り出されます。
a={ println "hello" }
このようにクロージャは変数に代入することができますし、 メソッドの引数に渡したり、 以下のように呼び出したりすることができます。
closure.groovy
a={ println "hello" } a();
これを実行すると以下のようになります。
> groovy closure.groovy
hello
クロージャは引数を取ることができます。
closure2.groovy
a={| p | println "hello ${p}" } a("universe"); a("hoge");
ちなみにこのようにPerlやRubyのように文字列中に${と}でくくることで変数pの値を展開することができます。 なお${と}の間には変数だけでなく任意の式が書けます。実行結果は以下のとおり。
> groovy closure2.groovy
hello universe
hello hoge
クロージャはJavaでいう無名内部クラス(annonymous inner class)のようなものです。
LispやPythonでいうlambdaのようなもの、あるいはCommon Lispやschemeでいうclosureのようなものです(そのまま)。
クロージャをよく使うのは繰り返し処理の場合です。 前述のlist.groovyの例はクロージャとeachメソッドを使って以下のように書くことができます。
each.groovy
mylist = [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文のような制御構造は使われていないのです。

クロージャを使ったこのような繰り返しはイテレータのありかたのひとつで内部イテレータと呼びます。これに対してjava.lang.Iteratorのようなものを外部イテレータと呼びます。

ちなみに以下のように [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.groovy
p = (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]

4. 特徴

以下にGroovyの特徴や気づいた点を列挙します。

4.1 実行に関するもの

4.2 言語機能に関するもの

4.3 ライブラリやフレームワーク

5. 欠点

以下にGroovyの悪い点やつかってて感じた首をひねらざるを得ない点などを列挙します。 現行バージョンに対するもので将来的に解決される可能性のあるものももちろん含まれています。(実際、このリストは当初書いたときから、Groovyがbeta4,beta5,beta6と版を重ねるにつれて修正しているのですが、その都度個数や度合いが減少しています)

6. 適用領域

以下にGroovyが向いていると思われる適用領域を示します。

6.1 テスト駆動開発アプローチ

Groovyが向いた適用領域としてはテストコードの記述があります。 テストコードはすばやく軽く作る必要があるし、 タイプセーフ性とか多人数開発とかは あまり関係がありません。Java開発のお供として、 Groovyはテストコード用途に急速に受け入れられるのではないでしょうか。

6.2 ソフトウェアのコンフィグレーション

Groovyの役割はこの側面からはいくつかあって、

ただ最後のは諸刃の剣ですね。設定するためだけに言語の習得を強要する、 敷居の高いシステムになりかねません。

7. まとめ

7.1 スクリプト言語の隆盛が意味するもの

オブジェクト指向言語に限っても、厳しい型付けを行う言語と動的な型付けを行う言語では、 それぞれの支持者の間で長い激しい論争が繰り返し行われてきました。 方や、厳密な型は安全なプログラムを作るために重要だといい、 方やそうじゃなくてすばやく動作させてテストを網羅的に きちんとやるほうがいい、というようなことです。前者の代表はJavaやC++であり、 後者はSmalltalkやSqueak, Python、Ruby流ということになります。

さて、この論争はどういう結果になったでしょうか? 驚くべきことにこれらの相反するアプローチのどうやら両方が正しい、ということを 現実が示しているように思えます。 WebアプリケーションのCoolなサイトは多くはPythonやPHPなどのスクリプト言語で書かれていますし、 企業システムやサーバアプリケーションではJavaの優勢は揺らぎそうにありません。 言語の優劣をその普及度で計ることができないのかもしれませんが、 少なくともどちらかが衰退するということはまったく起きていないのです。

月並みな結論ですが、単一の言語ではだめで やはり適材適所なのだということです。 そして、GroovyはJavaの厳格な世界に動的アプローチを注入し、 両者の組み合わせで最高の生産性を与えようというものです。 ここで重要なのは、GroovyはPythonユーザやRubyユーザが作ったのではなく、 RubyやPythonのことがうらやましくてしょうがないJava開発者がJavaのためのみに作ったものであるということです。 Groovyの開発者や潜在ユーザからは 「これでRubyを見て悔しい思いをしなくてすむ」 という切実で身勝手な思いが伝わってきます。

スクリプト言語とJavaの組み合わせについては古くはPnutsや近くはJRuby、Jythonなど多くがあります。しかし、(1)Javaのオブジェクトと完全に透過的に運用できる(2)独立した言語としてJavaでできることはクラス定義を含めて全部やれる、というアプローチをここまで明確にとったのはGroovyがはじめてです(たぶん)。
この結果、GroovyはJava技術がつち使ってきた既存の言語インフラストラクチャ (ライブラリ、ツール、開発ノウハウ、コミュニティ)をそのまま受け継いだ上 で最高の生産性を発揮できうるものになっている、といえるでしょう(現在の 実装でそれが実現できているかといわれるとできていないわけですが、可能 性として垣間見させてくれます)。

7.2 究極のEoD

近年、Sunの提唱するEoD(Ease of Development;開発を簡単にしよう)構想に基づき、 さまざまな拡張や提案がJavaの世界でなされてきています。例えば PHPとJavaを連携させる JSR 223であるとか、 JDK 1.5 "Tiger"でのJava言語仕様自体の一連の拡張とか、JSP ELとかです。

Groovyはこのような流れに沿うものですが、 これらと比べて最もラジカルで根本的なアプローチに特徴があるように思います。 どのぐらい根本的かというと いままで営々と構築・蓄積されてきたJava言語というものを構文レベルでいじりまくり、 躊躇無く改良しまくり、否定すらしているんではないかと感じられるほどの勢いです。

しかしこの改革はJava技術の表層である言語構文の側面のみに対するものであって、 GroovyはそのインフラであるJVMの技術を実に有効に活用しています。Javaの 価値とは何なのか、という問いに対してそれは表現ではなく実行モデルであり、 JVMであり、それらが提供するバイナリ互換性であり、プラットフォームニュート ラル性であり安全性だと、そういうことを提案している言語だといえます。

この最後のバイナリ互換の重要性に関する指摘はJames Strachan氏のWeblogでなされていたものです。

7.3 Rubyについて

Rubyは好きな言語ですが業務で使用するのはちょっと条件が厳しいというか人を選ぶところがあります。 端的にはいざ開発プロジェクトを始めるときに「Rubyができるひと5人ぐらいそろえてください」 と人材派遣会社に要望を出すわけにはなかなか行かないということです。 その点、GroovyはJavaの知識がほぼそのまま生かせる、ということは利点です。

昔を思い起こせばJavaの登場当初、「C++がわかってればJavaは2日で理解できる」 ということが言われたことがありましたが、 膨大なAPIを除いた言語仕様に関して言えば確かにそんな気がしたものでした。 同じことがGroovyにもいえて、Javaがわかっていれば (将来日本語ドキュメント等が整備されることを前提に)Groovyは基本的なところはまあ数日でたぶん理解できるでしょう。

Rubyはいい言語ですが良きにつけ悪しきにつけ、 まつもとゆきひろ氏の美学に基づく「個人の言語」なのだと思います。 Groobyは言語設計においては独自の美学を持たず、 「既存言語からの言語機能の良いとこどり」に徹し、 「産業の言語」を目指しているように見えます。

8. インストール方法

本家GroovyのサイトのInstalling Groovyを参照のこと。 難しくないです。JDK1.4以降が必要です。

9. 参考にしたリンク