alt.lang.jreコラム:
Groovyに触ってみよう



文法は自由自在

 プログラミング言語の文法の柔軟さは、コーディング作業の効率を大きく左右する。Groovyでは、PythonやRuby、Smalltalkといった言語の影響で、ベースであるJava言語の構成要素やコアライブラリの扱いを大幅に単純化している。ここでは、Groovyの文法が持つ柔軟性を示すため、クラスや関数(defキーワードで定義)、クロージャ、コレクション、レンジ、マップ、イテレータといった基本要素について紹介しよう。

クラス

 Groovyのクラスは、バイトコードのレベルではJavaクラスそのものである。ただ、アクセス修飾子を明記しない限り、デフォルトですべてのクラスはpublicクラスになる。またクラスのフィールドやメソッドは動的型付けの対象となる。returnキーワードも省略できる。

 リスト7は、Groovyでのクラス定義の例である。このDogクラスは、Dogオブジェクトのフルネームを表すStringを返すgetFullNameメソッドを備える。なお、すべてのメソッドは自動的にpublicメソッドとなる。

リスト7 GroovyクラスDogの例

class Dog{
  name

  bark(){
    println "RUFF! RUFF!"
  }
  
  getFullName(master){
    name + " " + master.lname
  }
  
  obeyMaster(){
    println "I hear you and will not obey."
  }
}

 続いてリスト8では、次のステップとして、2つのプロパティfnameとlnameを持つDogOwnerクラスを定義している。このとおり、いずれのクラスもシンプルに定義できる。

リスト8 GroovyクラスDogOwnerの例

class DogOwner{
  fname
  lname

  trainPet(pet){
    pet.obeyMaster()
  }
  
}

 次のリスト9は、DogクラスとDogOwnerクラスのインスタンスに対して、プロパティ をセットしたりメソッドを呼び出したりする例である。ここまでのコードを見れば、JavaクラスよりもGroovyクラスの方がより簡単に作成できることは明らかだろう。newキーワードは省略できないものの、型宣言は不要であり、プロパティ(自動的にpublicとして扱われる)への値の設定も労せずして記述できる。

リスト9 Groovyクラスの利用例

myDog = new Dog()
myDog.name = "Mollie"

myDog.bark()
myDog.obeyMaster() 

me = new DogOwner()
me.fname = "Ralf"
me.lname = "Waldo"

me.trainPet(myDog)

str = myDog.getFullName(me)
println str  // Mollie Waldoと表示

 ここで、getFullNameメソッドが戻り値として「Mollie Waldo」というStringオブジェクトを返す際に、returnキーワードを省略できる点にも注目してほしい。

ファーストクラスオブジェクトについて
「ファーストクラスオブジェクト」とは、実行時に生成して利用できるオブジェクトを指す。メソッドの引数として渡したり、変数に代入したり、ほかのオブジェクトに戻り値として返せたりできる。ちなみに、int型やboolean型などJava言語のプリミティブ型は、ファーストクラスオブジェクトとは見なされない。純粋なオブジェクト指向を好む技術者はこれを欠点としてとらえ、Java言語が真のオブジェクト指向言語ではない理由として引き合い出されることもあった。だがGroovyでは、数値を含むすべてをファーストクラスオブジェクトとして実装しており、この欠点を解消している。

関数定義(def)

 Groovyでは、defキーワードを用いることで、クラスの外側で関数を定義することができる。ほかのいくつかのスクリプト言語と同様に、Groovyではすべてをオブジェクト(前述の「ファーストクラスオブジェクトについて」を参照のこと)として実装しており、関数もオブジェクトとして扱うことができる。先に示したリスト3リスト4では、defキーワードにより関数を定義したり使用したりする例を示した。手短なスクリプトを組み立てるとき、Groovyの関数は大変便利だ。

クロージャ

 Groovyの最もパワフルで注目すべき機能は、クロージャである。クロージャは、Java言語の匿名クラスに似たファーストクラスオブジェクトで、匿名クラスのようにひとまとまりのコードを表すことができる。ただしこの両者には若干の違いがある。例えばクロージャでは、変数の値を自動的に受け取ったり渡したりできる。またクロージャには名前を付けることができ、再利用も可能だ。さらに重要なポイントは、匿名クラスよりもはるかに使い勝手がよい点だ。

 リスト10は、クロージャの能力を示すコード例である。この例では、Dogクラスに改良を加え、Dogインスタンスにセットされたクロージャを実行するtrainメソッドを定義している。

リスト10 クロージャの利用例

class Dog{  
  action

  train(){
    action.call()
  }
}

sit = { println "Sit, Sit! Sit! Good dog"}
down = { println "Down! DOWN!" }


myDog = new Dog(action:sit)
myDog.train()  // Sit, Sit! Sit! Good dogと表示

mollie = new Dog(action:down)
mollie.train() // Down! DOWN!と表示

 クロージャは引数を取ることができる。リスト11は、2つの引数(locationおよびxml)を取るpostRequestクロージャを定義した例である。このクロージャは、Jakarta Commons HttpClientライブラリ(http://jakarta.apache.org/commons/httpclient/)を用いてXML文書を指定されたサーバに送信する。その後、サーバからのレスポンスを収めたStringを戻り値として返す。クロージャ定義の下にあるコード例を見れば、クロージャを関数と同じように呼び出せることが分かるだろう。

リスト11 引数を持つクロージャの利用例

import org.apache.commons.httpclient.HttpClient
import org.apache.commons.httpclient.methods.PostMethod

postRequest = { location, xml |

  clint = new HttpClient()
  mthd = new PostMethod(location)  
  mthd.setRequestBody(xml)
  mthd.setRequestContentLength(xml.length())
  mthd.setRequestHeader("Content-type", 
     "text/xml; charset=ISO-8859-1")

  statusCode = clint.executeMethod(mthd)
  responseBody = mthd.getResponseBody()
  mthd.releaseConnection()
  return new String(responseBody)   
}

loc = "http://localhost:8080/simulator/AcceptServlet/"
vxml = "<test><data>blah blah blah</data></test>"

str = postRequest(loc, vxml)
println str

オートボクシング(autoboxing)
「オートボクシング」もしくは「ボクシング変換」とは、int型やdouble型、boolean型などのプリミティブ型を、java.langパッケージ内にある適切なラッパー型に自動変換する処理を指す。J2SE 1.5から提供される同機能を利用すれば、プリミティブ型のために型変換を書く労力が軽減される。

コレクション

 プログラマであれば、リストやマップなどのデータ構造でオブジェクトをまとめるコードを毎日のように書いているだろう。そこで多くの言語と同様に、Groovyもそうしたコレクションを扱うための豊富なライブラリを提供している。PythonもしくはRubyに触ったことがあれば、Groovyのコレクションの文法はとても親しみやすいはずだ。例えばリスト12に示すように、Java言語の配列に似た方法でリストを作成できる。なお、このリストの2番目の要素はIntegerにオートボクシングされることに注意してほしい。

リスト12 コレクションの利用例

collect = ['groovy', 29, 'here', 'groovy']

 Groovyのコレクションでは、リストを容易に作成できるだけでなく、リストを扱うための便利なメソッドもいくつか追加されている。これらを使えば、例えばそれぞれの値の出現回数をカウントしたり、リスト全体を1つに結合したり、リストをソートしたりといった処理を実に簡単に表現できる。その実例をリスト13に示す。

リスト13 Groovyコレクションの活用例

aCollect = [5, 9, 2, 2, 4, 5, 6] 

println aCollect.join(' - ')  // 5 - 9 - 2 - 2 - 4 - 5 - 6を表示
println aCollect.count(2)     // 2を表示
println aCollect.sort()       // [2, 2, 4, 5, 5, 6, 9]を表示

マップ

 リストと同様に、マップもとても簡単に利用できる。リスト14の例では、nameおよびdateというキーそれぞれにオブジェクトを割り当てたマップを作成している。この例が示すように、マップから値を得るには2つの異なる方法を利用できる。

リスト14 マップの利用例

myMap = ["name" : "Groovy", "date" : new Date()]

println myMap["date"]

println myMap.date

レンジ

 Groovyのコレクションを利用し始めると、「レンジ」の応用範囲の広さに気が付くはずだ。レンジとは、一定範囲の数値を順番に並べたリストのこと。リスト15に示すように、「..」を使えば終了値を含むレンジ(インクルーシブレンジ)を表し、「...」を使えば終了値を含まないレンジ(エクスクルーシブレンジ)を表す。

リスト15 レンジの利用例

myRange = 29...32
myInclusiveRange = 2..5

println myRange.size() // 3と表示
println myRange[0]   // 29と表示
println myRange.contains(32) //falseと表示

println myInclusiveRange.contains(5) //trueと表示

レンジによるループ

 レンジを使えば、ループをより上手に実装することもできる。リスト16の例では、エクスクルーシブレンジを変数aRangeとして定義し、a、b、c、そしてdを表示するループを記述している。

リスト16 レンジによるループの例

aRange = 'a'...'e'

for (i in aRange){
  println i
}

コレクションのその他の機能

 Pythonやその他のスクリプト言語になじみのない読者にとっては、Groovyのコレクションに備わるいくつかの機能が目を引くはずだ。例えばリスト17のように、作成したコレクションに対して負の数値のインデックスを用いることができる。

リスト17 負の数値によるインデックス

aList = ['python', 'ruby', 'groovy']

println aList[-1] // groovyと表示
println aList[-3] // pythonと表示

 またGroovyでは、レンジを利用してリストをスライスすることもできる。これにより、リストのサブセットを得ることができる(リスト18)。

リスト18 レンジによるリストのスライス

fullName = "Andrew James Glover"

mName = fullName[7...13]

print "middle name: " + mName // Jamesと表示

Ruby風コレクション

 Groovyのコレクションは、Rubyのコレクションと同じ文法で扱うことも可能だ。「<<」で要素を追加したり、「+」で結合したり、「-」でコレクション間の差分を得たりなど、Ruby風の文法を利用できる。またリスト19に示すように、「*」によりコレクションの繰り返しを表現でき、「==」によりコレクション同士を比較できる。

リスト19 Ruby風コレクション

collec = [1, 2, 3, 4, 5]
collec << 6 //collecに6を追加

acol = ['a','b','c'] * 3 //acolには9個の要素が入る

coll =  [10, 11]
coll2 = [12, 13]

coll3 = coll + coll2 //10,11,12,13

difCol = [1,2,3] - [1,2] //difColは3

assert [1, 2, 3] == [1, 2, 3] //true

イテレータ

 Groovyでは、リストや文字列などのさまざまなシーケンスに対する繰り返し処理を端的に記述できる。例えば文字列を順次処理するには、リスト20のようなコードを書けばよい。お気付きのとおり、Groovyのループ文forは、従来のJavaよりも自然な形でループを記述できる。

リスト20 イテレータの利用例

str = "uncle man, uncle man"

for (ch in str){
  println ch
}

 またGroovyのコレクションは、各要素にクロージャを適用するためのeachメソッドやfindメソッドを備えている。リスト21を見れば、クロージャを使ってコレクションを順次処理するというテクニックは無数の可能性を秘めていることが分かるだろう。

リスト21 クロージャによるイテレータの利用例

[1, 2, 3].each {  
  val = it 
  val += val
  println val
}

[2, 4, 6, 8, 3].find { x |
     if (x == 3){
       println x
     }
}

 リスト21前半の例では、イテレータとしてeachメソッドを利用している。クロージャの内部では個々の要素の値を加算し、6という結果が得られる。一方、後半のfindメソッドの例はさらに簡単だ。クロージャは各要素を順番に受け取り、その値が3であるかどうかをチェックする仕組みである。

3/4

 INDEX

Groovyに触ってみよう
  Page1
なぜまた新しい言語が必要か
  Page2
javacが不要
動的型付けの威力
Page3
文法は自由自在
  Page4
Groovyのその他の特徴

Java Solution全記事一覧





Java Agile フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Java Agile 記事ランキング

本日 月間