BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Groovy 1.5の新機能

Groovy 1.5の新機能

Groovy (サイト・英語)は、JVM上で動作するJavaライクな動的言語です。良いワインのように、時間を経て成熟してきました。2007年1月のGroovy 1.0リリースの成功後、1.5と名付けられた次のメジャーなマイルストーンはすでに発表されていました。Groovy 1.5は、この記事でこれから試すいくつかの興味深い新機能を備えています。言語仕様に対して加えられた最も大きな変更は、アノテーションやジェネリックス、Enumと言ったJava 5の機能をsポートしたことで、GroovyはSpringやHibernate、JPA、Google Guice、TestNGと言ったフレームワークをフルにサポートする唯一のJVM向け動的言語になりました。Java 5の機能以外にも、動的なふるまいをよりパワフルにカスタマイズ出来るようになり、より強力になったSwing UIビルダーやツールサポートの改善とともに、言語仕様においてわずかな文法拡張が施されています。

よりGroovyっぽいGroovy、そしてなぜそれが重要なのか

常にGroovyが最も売りとしている点は、Javaとのシームレスな統合です。GroovyクラスとJavaクラスを混ぜて使うのは非常に簡単です。 Javaクラスを継承し、GroovyインターフェースをインプリメントしたGroovyクラスなどを作ることもできます。他のJVM言語のほとんどは不幸にも、二つの異なる言語で作られたクラスをシームレスにやり取りすることはできません。そのため、あなたが持つ素晴らしいクラス階層を損なうことなく、仕事に最も適する言語を使いたいと望んでいるのなら、選択肢は多くありません。そしてGroovyは、最も透過的な方法で両方の言語を統合するための、あらゆる自由を保証します。

Groovy はJavaと同じライブラリ、同じオブジェクトモデル、同じスレッドモデル、同じセキュリティモデルを共有します。ある意味では、Groovyはあなたの Javaプロジェクトの実装の詳細だと考えることができ、解決の難しいインピーダンスミスマッチの問題を抱えることはありません

GroovyはJava であり、GroovyはJavaをより素晴らしいものにします。他の言語と比べると、GroovyはJavaと非常に似た文法を採用したおかげで、 Java開発者にとって最も緩い学習曲線を提供する言語であることは間違いありません。

さらに重要なこととして覚えておくべきなのは、Groovyは通常のJavaバイトコードを生成し、通常のJDKライブラリを使用するということです。そのため、あなたは新しいAPIをまるごと学習したり、統合に関して複雑なメカニズムを持つ必要がありません: 追加の作業なしに、GroovyとJavaは相互に変換可能なのです。さらなるメリットとしては、開発者のJavaスキル高いアプリケーションサーバ、サードパーティ製や独自開発したライブラリなどに対して行った投資を無駄にせずに済む、と言うことなのです。こうしたものは全て、Groovyを使えば問題なく再利用できます。

JDK やサードパーティ製 (もしくは独自開発の) ライブラリを呼び出す際、強い型付けをサポートしていない別の言語は、全てのJavaメソッドを呼び出せるわけではありません。それは、同じメソッドのポリモーフィックなバリエーション (訳注:メソッドオーバーロードのこと) の中から目的のメソッドを選ぶことができない、と言う事実に拠ります。生産性の向上や、コードをより読みやすくするために他の言語を選ぶ際、他のJava クラスを呼び出す必要があるなら、言語の選択を非常に慎重に行う必要があります。さもないと、途中で何度も行き詰ってしまうかも知れません。

今日では、あらゆるメジャーなエンタープライズフレームワークはアノテーションやEnum、ジェネリックスと言った機能を必要としており、それらが使用される範囲は最大限広げられつつあります。幸運なことにGroovy 1.5ではこうしたJava 5の機能が全てサポートされ、開発者はプロジェクト内においてそのメリットを享受することができます。アノテーションやEnum、ジェネリックスが Groovyでどのように使われているかを見ていきましょう。

Java 5の追加機能

Groovy コンパイラは常に、より古いJava VMと互換性のあるJavaバイトコードを生成しますが、Groovyが使用するコアライブラリはJDK1.4に依存しています。しかし、これらJava 5の追加機能のうち一部の機能のためには、Java 5のバイトコードを使用する必要があります。例えば生成されるクラスは、ランタイムリテンションポリシー付きのアノテーションを表すバイトコード情報を含むかも知れません。Groovy 1.5はJDK 1.4上で動かすことができますが、いくつかの機能はJDK 5上でしか利用できません。こうしたケースについては、この記事で解説します。

可変長引数

Java 5では、可変長引数を持つメソッドを表すための省略記法が導入されました。小さな三つのドットにより、Javaはメソッド引数の最後に同じ型のパラメータを複数指定できるようになりました。実際は、そうした可変長引数は単にその型の配列要素となります。可変長引数はGroovy 1.0ですでに存在しましたし、JDK 1.4ランタイムでも動作します。とはいえ、可変長引数の使い方をお見せするのは良いことです。基本的に、メソッドの引数の最後がオブジェクトの配列であるか、三つのドットでパラメータが宣言されていればいつでも、複数のパラメータをこのメソッドに渡すことができます。

最初のサンプルは、Groovyにおける可変長引数の使い方を省略記法で示しています。

int sum(int... someInts) {
def total = 0
for (int i = 0; i < someInts.size(); i++)
total += someInts[i]
return total
}

assert sum(1) == 1
assert sum(1, 2) == 3
assert sum(1, 2, 3) == 6

このサンプルで使用されているアサーションは、いくらでも好きなだけint型の引数を渡すことができると言うことを示しています。よりJavaと文法上の互換性を持たせるため、昔ながらのforループがGroovyに追加されたのも興味深いです - 様々な配列やコレクション型を繰り返すための透過的な方法として、inキーワードと言うより洗練されたバージョンが存在するにも関わらず、です。

以下のように、メソッドの宣言において最後のパラメータを配列にすれば、可変長引数をサポートできることに注意してください。

int sum(int[] someInts) { /* */ }

このコードは非常に平凡なものですし、合計を計算したいのなら、明らかにより表現力に富む方法が存在します。例えばあなたが数値のリストを持っていたとして、あなたはそれらのすべての合計を一行で求めることができます。

assert [1, 2, 3].sum() == 6

Groovyで可変長引数を扱うには、以下のセクションで見るアノテーションのように、JavaランタイムとしてのJDK 5は必要ありません。

アノテーション

JBoss Seam(source)ではエンティティ、コントローラ、コンポーネントをGroovyで書くことができますが、そのドキュメントで見られるように、@Entity, @Id, @Overrideなどのアノテーションで、あなたのBeanを修飾することができます。

@Entity
@Name("hotel")
class Hotel implements Serializable
{
@Id @GeneratedValue
Long id

@Length(max=50) @NotNull
String name

@Length(max=100) @NotNull
String address

@Length(max=40) @NotNull
String city

@Length(min=2, max=10) @NotNull
String state

@Length(min=4, max=6) @NotNull
String zip

@Length(min=2, max=40) @NotNull
String country

@Column(precision=6, scale=2)
BigDecimal price

@Override
String toString() {
return "Hotel(${name}, ${address}, ${city}, ${zip})"
}
}

Hotel エンティティは@Entityアノテーションでマークされており、@Nameによって名前を与えられています。検証目的で数値の上限と下限に制約を設けている@Lengthアノテーションのように、異なるパラメータをアノテーションに対して与えることも可能です。また、Groovyプロパティが使われていることにも注意してください: getterやsetterはどこにあるんでしょう?publicやprivateと言ったアクセス修飾子はどこに行ったのでしょう?プロパティが導入されるのをJava 7や8まで待つ必要はありません!規約により、プロパティの定義はString countryのように単純です: privateなcountryフィールドは自動的に生成され、同様にpublicなgetterやsetterも生成されます。あなたのコードは自然と、より簡潔で読みやすくなります。

アノテーションはJavaと同じくクラス、フィールド、メソッド、メソッド引数に付与することができます。しかし、知っておいてほしいことが二つあります。まず一つは、あなたはアノテーションをGroovyで使用することができますが、それを定義することはできないということです - しかし、Groovyの将来バージョンではそれが可能になるでしょう。二番目に、ほとんど100%Javaと同じシンタックスですが、アノテーションの引数に値の配列を渡す際わずかな違いが存在するということです: 要素を囲むために中カッコを使用する代わりに、より統一されたシンタックスを提供するため、Groovyは大かっこを使用する必要があるということです。同様に、Groovyのリストと配列は、その要素を囲むために大かっこを使用するからです。

Groovy 1.5のアノテーションにより、JPAやHibernateのアノテーションが付与されたBeanを簡単に定義することができます(http://www.curious-creature.org/2007/03/25/persistence-made-easy-with-groovy-and-jpa/) (英語)。GroovyでSpringのサービスに@Transactionalアノテーションを追加し、TestNGとFestを使ってSwing UIをテストしてみてください(http://www.jroller.com/aalmiray/entry/testing_groovy_uis_with_fest)(英語)。アノテーションを活用している便利でパワフルなエンタープライズフレームワークを、全てあなたのGroovyプロジェクトから使用することができるのです。

Enum

同じ型を持つ定数の決められた集合が必要なら、Enumが便利です。整数型の定数を使わずに、定数を定義するクリーンな方法をずっと必要としていましたか?ならば、Enumはあなたの友達です。以下のコードは曜日を定義する方法を示しています。

enum Day {
SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
THURSDAY, FRIDAY, SATURDAY
}

一度Enumを定義すれば、Java内でDay.MONDAYのような通常の表記方法を用いて使用することができますし、switch / caseステートメントをより活用することもできます。

def today = Day.SATURDAY
switch (today) {
// Saturday or Sunday
case [Day.SATURDAY, Day.SUNDAY]:
println "Weekends are cool"
break
// a day between Monday and Friday
case Day.MONDAY..Day.FRIDAY:
println "Boring work day"
break
default:
println "Are you sure this is a valid day?"
}

Groovy のswitch文は、Cライクな言語のswitchよりも少しだけパワフルなことに注意してください。switchとcaseには、どのような種類のオブジェクトを使用することも可能です。列挙された各々の値で7つの異なるケースブロックを積み上げる代わりに、リストや範囲でグループ化することができます: 値がリストや範囲の中にあればそのケースは真となり、関連付けられた命令が実行されます。

Javaチュートリアルに触発されたより複雑な例では、Enumがとても奥の深いものだということを示しており、Enumがどうやってプロパティやコンストラクタ、メソッドを持つことができるかを表しています。

enum Planet {
MERCURY (3.303e+23, 2.4397e6),
VENUS (4.869e+24, 6.0518e6),
EARTH (5.976e+24, 6.37814e6),
MARS (6.421e+23, 3.3972e6),
JUPITER (1.9e+27, 7.1492e7),
SATURN (5.688e+26, 6.0268e7),
URANUS (8.686e+25, 2.5559e7),
NEPTUNE (1.024e+26, 2.4746e7)

double mass
double radius

Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}

void printMe() {
println "${name()} has a mass of ${mass} " +
"and a radius of ${radius}"
}
}
Planet.EARTH.printMe()

アノテーションと同様Java 5のバイトコードが生成されるため、GroovyでEnumを使用するにはJDK 5以上を必要とします。

静的インポート

先に示したEnumの例では、列挙値に対して常に親Enumクラスの接頭辞を付ける必要がありましたが、静的インポート (JDK 1.4ランタイムでも動作します) のおかげで、Planetと言う接頭辞を省略していくらか文字を書かずに済みます。

import static Planet.*

SATURN.printMe()

もはやPlanetと言う接頭辞は必要ありません。しかしもちろん、静的インポートはEnumのためだけのものではありません。他のクラスや静的なフィールドに対しても動作します。ちょっとした計算をしたい場合はどうなるでしょう?

import static java.lang.Math.*

assert sin(PI / 6) + cos(PI / 3) == 1

java.lang.Mathの静的メソッドと定数のどちらも静的にインポートされ、式がより簡潔になっています。しかし、サインとコサインの省略形があなたにとって読みにくいのであれば、asキーワードを用いてGroovy内で別名を使用することができます。:

import static java.lang.Math.PI
import static java.lang.Math.sin as sine
import static java.lang.Math.cos as cosine

assert sine(PI / 6) + cosine(PI / 3) == 1

別名は、静的インポートのためだけではなく、通常のインポートでも同様に動作します。多くのフレームワークに見られるような、非常に長いクラス名に対してショートカットを追加したり、あまり明瞭ではない、もしくはあなたの標準命名規約に従っていない名前のメソッドや定数をリネームしたりするのにとても便利です。

ジェネリックス

Java 5で何かと論争の的になっている機能であるジェネリックスも、Groovy 1.5の最新リリースには含まれています。当初は、動的言語に対して型の情報をさらに追加するのは奇妙に思えたものです。Java開発者は通常、型消去 (Javaの古いバージョンとの後方互換性が理由) によって、ジェネリック型を表現するための情報はクラスのバイトコード内に残されていない、と考えています。しかしこれは間違いです。リフレクション APIを用いれば、あなたはクラスのフィールドやメソッド引数が持つジェネリック型の詳細を見るため、クラスをイントロスペクトすることができます。

例えばあなたがListと言う方のフィールドを宣言するとき、バイトコード内のどこかに、何らかのメタ情報の形式でこの情報は保持されますが、このフィールドな実際には単なる List型となります。リフレクションによって利用できるこの種の情報は、JPAやHibernateと言ったエンタープライズフレームワークによって使用され、要素のコレクションを持つエンティティから、要素の型によって表されるエンティティへと関連を作成することができるようになります。

これを実現するには、ジェネリックスの情報がクラスフィールドに保持されているかどうかをチェックしましょう。

class Talk {
String title
}

class Speaker {
String name
List talks = []
}

def me = new Speaker(
name: 'Guillaume Laforge',
talks: [
new Talk(title: 'Groovy'),
new Talk(title: 'Grails')
])

def talksField = me.class.getDeclaredField('talks')
assert talksField.genericType.toString() ==
 'java.util.Listt'

二つのクラスを定義しました: Speakerクラスは、会議でTalkを講演します。Speakerクラスの中では、talksプロパティはList型です。そして、name、talkプロパティを初期化するために、そしてTalkインスタンスのリストを作成するために二つのナイスなショートカットを使用して、Speakerインスタンスを作成します。一度このセットアップコードが準備できたら、講演内容を表現したフィールドにジェネリック型の情報が正しく保持されているかどうかをチェックします: ええ、talksListでしたが、TalksListでもありました。

共変の戻り値型

Java 5では、親クラスのメソッドと同じ名前・同じ引数型のメソッドをサブクラスに定義し、親メソッドの戻り値型から派生した戻り値の型であれば、親のメソッドをオーバーライドすることができます。Groovy 1.0ではこの「共変の戻り値型」はサポートされていませんでした。しかしGroovy 1.5では使うことができます。加えて、親クラスのメソッドが持つ戻り値型から派生した型以外でオーバーライドを試みると、コンパイルエラーが発生します。共変の戻り値型は、パラメタライズ化された型でも動作します。

言語仕様にいくつかの追加を行ってJava 5の機能をサポートする以上に、Groovy 1.5では他の文法拡張もわずかに加えられています。それらは、以降のセクションで見ていきます。

文法の追加

Elvis演算子

アノテーション、ジェネリックス、EnumをGroovyと言ったJava 5の機能をGroovyに持ち込むというのは別にして、新しい演算子が言語に登場しました: 「?:」Elvis演算子です。問題の演算子を見れば、なぜそれがそういうニックネームになったか、簡単に推測できるでしょう - もしわからないならスマイリーとして考えて見てください。この新しい演算子は実際、三項演算子のショートカット記法です。あなたは変数の値がnullだった場合に何かデフォルトの値を代入する、という目的のために何回三項演算子を使ってきましたか?Javaにおける典型的なケースは以下のようなものです。

String name = "Guillaume";
String displayName = name != null ? name : "Unknown";

Groovy では、必要に応じて型を"強制的に" boolean値に変換できるので (例えば、ifやwhileのように条件式を必要とされる箇所)、このステートメントではnullとの比較を省略できます。もし文字列がnullなら、それはfalseに強制変換されるので、Groovyではこのようになります。

String name = "Guillaume"
String displayName = name ? name : "Unknown"

しかし、あなたはname変数が繰り返されており、DRY (Don't Repeat Yourself) の原則を破っていることにきっと気づくでしょう。こうした構文はかなり一般的なため、Elvis演算子はこうしたよくあるケースを単純にするため生み出されました。ステートメントは以下のようになります。

String name = "Guillaume" 
String displayName = name ?: "Unknown"

二回目に出現していたname変数は単純に省略され、三項演算子はもはや三項ではなく、より簡潔な形に短縮化されました。

この新しい構文では副作用が存在しない、と言うことに気づくのもまた重要です。一番目の要素 (ここではname) は、二回評価されることがありません。三項演算子では、二回評価されてしまいました。そのため、三項演算子の第一要素を一回だけ評価した結果を保持するための、中間的な一時変数を作る必要がないのです。

昔ながらのforループ

Groovy はJavaの100%完全なスーパーセットではありませんが、Groovyの文法はリリースを経るごとにJavaの文法に近くなってきており、また JavaコードもますますGroovyにとって妥当なものになってきています。これによる真のメリットは、Groovyを始めたときにJavaコードを Groovyのクラスにコピー&ペーストすることができ、しかも期待したとおりに動くだろう、と言うことです。その後時間をかけて言語を学び、 Groovyにおいては強制されていないセミコロンを捨て去ったり、GString (補間された文字列) やクロージャを使ったり、と言った事を始めます。Groovyは、Java開発者にとってより平坦な学習曲線を提供します。

しかし、Cを背景としたJavaの昔ながらのforループがGroovyで許容されていないという事実は、Java文法との互換性を一部切り捨てるということでした。当初Groovyの開発者は、この文法はベストではないし、より読みやすいfor / in 構造を使う方が好みでした。しかし、「この古い文法構造もGroovyに含めてくれ」とGroovyのユーザが何度も頼んだ結果、開発チームはforループをGroovyに戻すことを決断したのです。

Groovy 1.5では、Groovyのfor / inか、昔ながらのforループを選ぶことができます。

for (i in 0..9)
println i

for (int i = 0; i < 10; i++)
println i

最終的には好みの問題ですが、長い間Groovyを使っているユーザは、より簡潔なfor /in ループを通常は好むようです。

名前付きパラメータでカッコが不要に

この簡潔かつ柔軟な文法、そしてそのダイナミックな能力により、Groovyは内部的なドメイン固有言語のための理想的な選択肢となりました。問題領域のエキスパートと開発者の間で共通のメタファを共有したい時に、アプリケーションのキーコンセプトとビジネスルールをモデリングするためのビジネス専用の言語を作るため、Groovyを活用することができます。こうしたDSLで重要な点は、非常に読みやすいコードを実現し、技術者でない人間でも簡単に書けるようにすることです。このゴールをさらに深く達成するために、名前付きパラメータをカッコで囲むことなしに使用できるよう言語仕様が調整されました。

Groovyでは当初、名前付きパラメータは以下のようなものでした。

fund.compare(to: benchmarkFund, in: euros)
compare(fund: someFund, to: benchmark, in: euros)

数値に新しいプロパティを加えることにより - Groovyではこうしたことが可能ですが、この記事で扱う範囲を超えています - 以下のようなコードを書くことも可能です。

monster.move(left: 3.meters, at: 5.mph)

今ではカッコを省略することができ、コードはさらに少しクリーンになります。

fund.compare to: benchmarkFund, in: euros
compare fund: someFund, to: benchmark, in: euros
monster.move left: 3.meters, at: 5.mph

明らかにこれは大した違いではありませんが、各ステートメントがよりプレーンな英語のセンテンスに近づき、ホスト言語が通常持つ決まりきった技術的なコードが除去されています。この小さなGroovy言語の文法拡張は、業務DSLの設計者に対してより広い選択肢を提供します。

ツールサポートの向上

Groovy がまだ出たての頃に致命的な問題だったのは、良いツールのサポートが欠如していたことです: ツールのチェインもIDEのサポートもありませんでした。幸福なことに、GroovyとGrails Webフレームワークが成熟と成功を収めたことで、こうした状況は変わりました。

"ジョイント"コンパイラの紹介

Groovy は、Javaとの透過的でシームレスな統合により良く知られています。しかしこれは単にGroovyスクリプトからJavaのメソッドを呼び出せると言うだけではありません。両言語間の統合はそれを超えるものです。たとえば、Javaクラスを継承してGroovyインターフェースを実装したGroovyクラスを作成する、もしくはその逆を行うことなどが全般的に可能です。不幸にも、他の言語の中にはこうしたサポートが完全ではないものもあります。しかし今まではGroovyとJavaのクラスをミックスして使用する際、双方のクラスをコンパイルするときは、コンパイル順序をうまく選択するよう注意を払う必要がありました。両言語の間で依存性の循環が発生すると、"鶏と卵"問題にぶち当たります。幸運なことにGroovy 1.5ではこれはもはや問題ではありません。JetBrains - Java IDEアワードの勝者であるIntelliJ IDEA(サイト・英語)のメーカー - による寄与のおかげで、"ジョイント"コンパイラが使えるようになりました。これにより、クラス間の依存性を考えることなく、GroovyとJavaのソースを一度にどちらもコンパイルすることができます。

もしあなたがジョイントコンパイラをコマンドラインから使用したいなら、ジョイントコンパイルを利用可能にする -j フラグを指定すれば、通常通りgroovycコマンドを呼び出すことができます。

groovyc *.groovy *.java -j -Jsource=1.4 -Jtarget=1.4

裏方として動作するjavacコマンドにパラメータを渡すため、フラグに J という接頭辞を付けることができます。AntやMavenのビルドファイルから、Antタスクを通じてジョイントコンパイラを使用することも可能です。

<taskdef name="groovyc"
classname="org.codehaus.groovy.ant.Groovyc"
classpathref="my.classpath"/>

<groovyc
srcdir="${mainSourceDirectory}"
destdir="${mainClassesDirectory}"
classpathref="my.classpath"
jointCompilationOptions="-j -Jsource=1.4 -Jtarget=1.4" />

Groovy向けのMavenプラグイン

Maven ユーザ向けに、Java / Groovyアプリケーションをビルドできるフル機能のMavenプラグインが、Godehausにて公開されています: GroovyとJavaコードをコンパイルし、JavaDocタグからドキュメントを生成し、Groovyを用いてMavenプラグインを書くこともできます。
また、Groovyプロジェクトをより素早く開始するためのMaven archetypeも存在します。より深い情報はプラグインのドキュメンテーションを見てください。 http://mojo.codehaus.org/groovy/index.html (英語)

GroovyDocドキュメンテーションツール

Java 開発者は、クラス、インターフェース、フィールド、メソッドのコメント内にJavaDocタグを書いて、コードのドキュメントを記述することができます。 Groovyでもそうしたタグをコメント内で使用することもできますし、GroovyDocと呼ばれるツールを用いることで、全てのGroovyクラスにおいてJavaDocドキュメントと同様の物を生成することができます。

Antタスクも存在し、ドキュメントの生成に用いることもできます。

<taskdef name="groovydoc"
classname="org.codehaus.groovy.ant.Groovydoc">
<classpath>
<path path="${mainClassesDirectory}"/>
<path refid="compilePath"/>
</classpath>
</taskdef>

<groovydoc
destdir="${docsDirectory}/gapi"
sourcepath="${mainSourceDirectory}"
packagenames="**.*" use="true"
windowtitle="Groovydoc" private="false"/>

新しい対話型シェルとSwingコンソール

Groovy ディストリビューションには、常に二つの異なるシェルが含まれています: Swingコンソールとコマンドラインシェルです。コマンドラインシェルであるGroovyshは、ユーザとのインタラクションと言う点から見るとそれほどフレンドリーではありません: ステートメントを実行したい時は常に、各ステートメントの後に'go'もしくは'execute'とタイプしなくてはなりません。素早いプロトタイピングや新しいAPIを試したい時などに、'go'と毎回タイプするのは非常に面倒です。こうした状況は、Groovy 1.5では新しい対話型シェルの登場により改善されています。もはや'go'とタイプする必要はありません。

この新しいシェルは、拡張された機能をいくつか持っています。ANSIに基づいた色付けを提供するJLineライブラリを使用、タブによるコマンド補完、ライン編集の機能と言ったものです。異なるスクリプトバッファで作業し、すでにインポートされたクラスを記憶し、既存のスクリプトをロードし、現在のスクリプトをファイルに保存し、コマンドの履歴を見ることなどもできます。サポートされている機能の詳しい説明は、こちらのドキュメント(source) を参照してください。

コマンドラインシェルだけが恩恵を受けたわけではなく、Swingコンソールも改善されています。新しいツールバー、進化したアンドゥ機能、フォントサイズの変更機能、シンタックスハイライトなどです。多くの改善がコンソールに施されています。

IntelliJ IDEA JetGroovyプラグイン

最も優れたツールによるサポートについて語るのは、このセクションの最後までとっておきました。JetGroovyプラグインです: フリーでオープンソースのIntelliJ IDEAプラグインで、GroovyとGrailsを徹底的にサポートしています。このプラグインはJetBrains自身によって開発され、言語と Webフレームワークの双方に対して無類のサポートを提供しています。

Groovyサポートにおける、利用可能な機能の一部を紹介します。

  • シンタックスハイライト 全てのシンタックスに対して行われます。さらに、型が不明な場合や、可能性のあるエラーを見つけてヘルプを表示するための静的な型情報が見つからない場合などに応じて、異なる警告を表示します。
  • Groovyクラスやスクリプト、Groovyで書かれたJUnitテストケースを実行する機能を持ちます。
  • デバッガ: JavaとGroovyのコードをまたいでステップ実行でき、ブレークポイントをセットし、変数の値や現在のスタックを見れたり、などなど
  • ジョイントコンパイラ: コンパイラはGroovyクラスとJavaクラスのどちらもコンパイルし、言語をまたいで依存性を解決できます。
  • パッケージ、クラス、プロパティ、フィールド、変数、メソッド、キーワードに対するコード補完、そして、そしてSwing UIビルダに対する固有のサポート
  • 非常に優れた、クラスの検索機能と使用法の表示機能
  • リファクタリング: Javaで使用できた、愛すべきリファクタリングのほとんどが利用でき、JavaとGroovyをまたいで動作します。"囲む"、様々な要素の生成、変数のリネームやインライン化、パッケージ、クラス、メソッド、フィールドのリネームなどです。
  • import文の最適化とコードのフォーマッティング
  • 構造ビュー: クラスの構造を鳥瞰できます。

結局、IntelliJ IDEAが持つJava⇔Groovyの相互作用やサポートのレベルを考えると、GroovyとJavaのどちらででクラスを書いているのかを気にする必要はないということです。Javaプロジェクトで一度Groovyを使ってみようかと考えていたり、Grailsアプリケーションの開発を計画しているなら、これは絶対にインストールすべきプラグインです。

JetBrainsのウェブサイト(source)に行けば、より詳しい情報を得ることができます。

IntelliJ IDEAのプラグインについてのみ述べましたが、Groovy開発のためにあなたの習慣を変える必要はありません。IBMのProject Zero開発者たちによって継続的に改善されているEclipseプラグインや、NetBeansにおけるSunの全く新しいGroovyとGrails のサポートを使うことができます。

パフォーマンスの改善

新しい機能とともに、Groovyの新しいリリースでは以前のバージョンに比べ、特筆すべきパフォーマンス改善とメモリ使用量の低下がもたらされました。私たちの非公式なベンチマークで、私たちのテストスイート全体にかかる時間を測ったところ、Groovy 1.5のベータバージョンに比べて15%から45%ほど速度が向上していることがわかりました。古いGroovy 1.0と比べるなら、より高い数値が期待できるのは確実です。より公式なベンチマークはまだ開発中ですが、こうした数値は、保険会社において彼らのポリシーリスク計算エンジンのビジネスルールをGroovyで書いている開発者たちや、高度に並列化されたマシンでいくつかのテストを走らせた他の企業によっても確認されています。全体的に、Groovy 1.5はほとんどのシチュエーションで早く、スリムになっているはずです。Groovyを使用している部分においては、単位時間当たりの処理量が恐らく変化することでしょう。

さらなるダイナミックな能力

GroovyとGrailsプロジェクトの間の緊密な協力関係を通じ、Grailsの心臓部が成熟したのを踏まえて、Groovyに新しいダイナミック機能が追加されました。

Groovy は動的な言語です: 分かりやすく言うとそれは、メソッドディスパッチが実行時に行われる、と言うような特定の事実を意味しています。Javaや他の言語のように、コンパイル時に行われるのではありません。メソッドディスパッチのロジックを担当する、MOP (Meta-Object Protocol) と呼ばれる固有のランタイムを持ちます。幸運にもこのランタイムは非常にオープンです。システムにフックをかけたり、通常の振る舞いを変更したりできます。各々のJavaクラス、そして各々のGroovyインスタンスは、オブジェクトの実行時における振舞いを表現したメタクラスに関連付けられます。いくつかの基底クラスを継承し、カスタムのメタクラスを定義することで、MOPと相互作用する様々な方法をGroovyは提供しています。しかしGrails プロジェクトの貢献のおかげで、より素晴らしいメタクラスが利用できます: expando メタクラスです。

コンセプトを理解する助けになるのは、やはりコードサンプルです。以下のサンプルでは、Stringインスタンスであるmsgは、metaClassプロパティを通じてメタクラスにアクセスされています。それからStringクラスのメタクラスを変更し、toUpperCase()のショートカット記法として、「up」と呼ばれる新しいメソッドをStringクラスに追加します。そして、メタクラスのupプロパティにクロージャを代入します。そのプロパティは、クロージャを代入することによって生成されます。このクロージャは引数をとらず (だから->で始められています)、クロージャのデリゲートが持つtoUpperCase()メソッドを呼び出しています。デリゲート (delegate) は、オブジェクトの実態 (ここではStringのインスタンス) を表す特別な変数です。

def msg = "Hello!"
println msg.metaClass

String.metaClass.up = { -> delegate.toUpperCase() }
assert "HELLO!" == msg.up()

このメタクラスを通じて、利用可能なメソッドとプロパティを問い合わせることができます。

// print all the methods
obj.metaClass.methods.each { println it.name }
// print all the properties
obj.metaClass.properties.each { println it.name }

特定のメソッドやプロパティが利用できるかチェックすることもできます。instanceofでチェックするより精度の高いチェックを行えます。

def msg = 'Hello!'
if (msg.metaClass.respondsTo(msg, 'toUpperCase')) {
println msg.toUpperCase()
}

if (msg.metaClass.hasProperty(msg, 'bytes')) {
println foo.bytes.encodeBase64()
}

これらのメカニズムはGrails Webフレームワーク内で広範囲に使用されています。、例えばダイナミックファインダを作成するためなどです: Bookドメインクラスに対してfindByTitle() と言った動的メソッドを呼びだすことができるため、ほとんどの場合DAOは必要なくなりました。Grailsはメタクラスを通じて、そうしたメソッドをドメインクラスに自動的に追加します。さらに、メソッドがまだ存在しない場合は初回の使用時に作成してキャッシュされます。これは以下で説明するように、さらに進んだフックにより達成されています。

先ほど見せた例以外にも、expandoメタクラスは補完的なフックをいくつか提供しています。4種類のメソッドがexpandoメタクラスには追加されています。

  • invokeMethod() 全てのメソッドコールをインターセプトできます。
  • methodMissing() 他のメソッドが見つからなかったとき、最終手段として呼び出されます。
  • get/setProperty() プロパティに対する全てのアクセスをインターセプトできます。
  • propertyMissing() プロパティが見つからないとき呼び出されます。

expando メタクラスを使用してアプリケーションが使用している型の振る舞いをカスタマイズすると、Groovyの前バージョンと比べてより開発が易しくなり、開発のための貴重な時間を節約できるようになります。こうしたテクニックが全ての人に必要とされているわけではないのは明らかですが、クラスをデコレーションするためのAOP (Aspect指向のテクニック) を適用したい場合や、決まり切った不必要なコードを削除することでアプリケーションの業務的なコードをより単純で読みやすくしたい場合など、多くの状況において便利です。

Swingをより強力にする

Groovy プロジェクトは、才能のあるSwing開発者チームを持つ機会に恵まれました。彼らはSwingでユーザインターフェースを構築するためのGroovyの機能を拡充するため、とても良く働いてくれます。SwingのUIを構築するための土台となるのはSwingBuilderクラスです: ソースコードの文法レベルで、Swingコンポーネントが互いにどうネストしているかを見ることができます。Groovyのウェブサイトにある単純なサンプルでは、小さなGUIをどのように単純に作成できるかを見ることができます。

import groovy.swing.SwingBuilder
import java.awt.BorderLayout

import groovy.swing.SwingBuilder
import java.awt.BorderLayout as BL

def swing = new SwingBuilder()
count = 0
def textlabel
def frame = swing.frame(title:'Frame', size:[300,300]) {
borderLayout()
textlabel = label(text:"Clicked ${count} time(s).",
constraints: BL.NORTH)
button(text:'Click Me',
actionPerformed: {count++; textlabel.text =
"Clicked ${count} time(s)."; println "clicked"},
constraints:BorderLayout.SOUTH)
}
frame.pack()
frame.show()

目新しいのは、Swingビルダのコンセプトが、カスタムのコンポーネントファクトリを提供するよう拡張されていることです。デフォルトではGroovyにバンドルされていませんが、JIDEやSwingXと言ったプロジェクトと、通常のSwingビルダのコードを統合することのできる追加モジュールが存在します。

しかしこのトピックはそれだけで一本の記事に値するので、私はこのリリースにおける他の改善をいくつかご紹介しようと思います。たとえば、 bind()メソッドです。これはBeans Binding (JSR-295) に着想を得ており、お互いに加えられた変更に反応するようBeanとコンポーネントを簡単にバインドすることができます。以下の例では、スライダコンポーネントの値に従ってボタンのインセット値が変化します。

import groovy.swing.SwingBuilder
import java.awt.Insets

swing = new SwingBuilder()
frame = swing.frame {
vbox {
slider(id: 'slider', value:5)
button('Big Button?!', margin:
bind(source: slider,
sourceProperty:'value',
converter: { [it, it, it, it] as Insets }))

}
}
frame.pack()
frame.size = [frame.width + 200, frame.height + 200]
frame.show()

コンポーネント同士をバインドするのは、ユーザインターフェースを構築する時にはありふれたタスクです。このバインディングメカニズムを使うと、こうしたタスクが単純化されます。自動バインディングに使用できるオプションは他にもありますが、それも専門的な記事に任せた方がよさそうです。

新しくて注目に値する他の機能としては、悪名高いSwingUtilitiesクラスを呼び出したり新しいスレッドを開始したりするために、クロージャを応用した新しい便利なメソッドが追加されていることです。edt()はinvokeAndWait()を呼び出し、doLater()invokeLater()を呼び出し、doOutside()は新しいスレッド内でクロージャを実行します。醜い匿名内部クラスを使用する必要はもうありません: こうしたショートカットメソッドを通じて、クロージャを使うだけです!

大事なことを言い忘れていましたが、SwingBuilderのbuild()メソッドのおかげで、ビューの記述と、それに関連付けられた振る舞いのロジックを分離するのが簡単になりました。コンポーネント間の相互作用やバインディングをメインクラスに置きつつ、ビューのみを含む分割したスクリプトを作成することができ、MVCモデルに基づいた明確な分離を行うことができます。

まとめ

この記事では、注目に値する新機能のあらましを説明しました。しかし、私たちはGroovyの新しいバージョンの、表面をかろうじて引っかいたにすぎません。大きなハイライトとしては、主に新しいJava 5対応機能に関するものでした。アノテーション、Enum、ジェネリックスと言ったものです: これによりGroovyは、Spring、Hibernate、JPAと言ったエンタープライズフレームワークと申し分なく、かつシームレスに統合されうる可能性を完璧に手に入れました。文法の改善や、動的能力の拡張により、Groovyは、アプリケーションの拡張ポイントと簡単に統合しうる組み込みのドメイン固有言語を作成し、ビジネスロジックをカスタマイズできるようにします。ツールのサポートにつぎ込まれた労力により開発者のエクスペリエンスは明らかに向上し、Groovyの採用にはもはや何の障害もないように見えます。全体的に見てGroovy 1.5は、開発者の人生を楽にするというゴールは十分に満たされており、GroovyがJava開発者のツールボックスの一部となるのは確実だと言えます。

著者について

Guillaume LaforgeはGroovyのプロジェクトマネージャーであり、JSR-241 - Javaコミュニティプロセスにおける、Groovy言語の標準化のためのJava Specification Request - のスペックリードです。彼はまた、GroovyとGrailsプロジェクトによる開発を支持し、リードする企業、G2One, Inc(サイト・英語)の技術最高責任者であり共同設立者でもあります。GuillaumeはJavaOne、JavaPolis、Sun TechDays、Spring Experience、Grails eXchangeと言った様々なカンファレンスで、普段からGroovyとGrailsについて語っています。

原文はこちらです:http://www.infoq.com/articles/groovy-1.5-new

この記事に星をつける

おすすめ度
スタイル

BT