この記事は、Java Advent Calendar 2012 の第9日目です。
前の記事は すふぃあ氏(@empressia)の記事 です。
明日の記事は 谷本 心氏(@cero_t) です。
Java に Lambda が来る!
Java 8 に Lambda が入ることはまことに喜ばしい限りですね。でも、使えるのはまだちょっと先のことになりますし、Java 8でホントに入るの?という心配もあります (^_^A;Java 8 が出ても、すぐには使えない仕事も多いですしね。Java プロジェクトで実際に Lambda を使うのは、しばらく先のことになってしまうかもしれません...。
そこで Xtend です!
Java プロジェクトで Xtend を使えば、今すぐに Lambda を使えます!Xtend については、以前書いた記事(社内向けのXtend紹介資料) を参考にしてもらうことにして、今日は Xtend の Lambda とストリーム処理のシンプルさについて、ご紹介しようと思います。
Xtend の Lambda の基本
Xtend の Lambda は、Java のそれと同じく、単一メソッドインタフェースに対するシンタクスシュガーです。Xtend の Lambda は、Java と同じイディオムで使用でき、相互運用性もある点で便利です。ただ、記法は異なります。
従来の Java の以下のような局面では、Lambda を使うことができます。
List<Person>men = ids.map(new Function1<ID, Person> () { @Override public Person apply(ID id) { return Person.get(id); } }).filter(new Function1<Person, Boolean> () { @Override public Boolean apply(Person p) { return p.sex == MALE; } }});
Java 8 の Lambda ならば、以下のように書くでしょう。
List<Person>men = people.map(id -> Person.get(id)).filter(p -> p.sex == MALE)
Xtend では以下のように書けます。
ポイントは、型推論と暗黙引数 it ですね。val men = ids.map[id| Person::get(id)].filter[p| p.sex == MALE]
val men = ids.map[Person::get(it)].filter[sex == MALE]
Xtend の Lambda は、-> 記号ではなく、[]で囲んだ形になっています。
これは一見妙な感じもしますが、DSL 風の API を Lambda で作るには、結構向いているのです。
Xtend の関数型変数の記述
Xtend で関数型変数を表すシュガーは以下のようになりますが、型推論でより簡単に記述できます。val (ID)=>Person mapper = [Person::get(it)]
val predicate = [Person p| p.sex == MALE]
Xtend の遅延コレクション評価は Stream 要らず
Java 8 では、遅延コレクション評価のために、Stream が追加されます。曰く、無限データストリームを処理するのには遅延評価()が必要だが、外部イテレータでは記述が面倒になるので、遅延的な内部イテレータを提供する新しい仲介型が必要だ、ということです。Streamを返すように、既存のコレクション実装を修正すれば、皆が幸せになれると。
Xtend も、遅延コレクション評価の簡潔な記述を提供します。
しかし、Xtend が Java 8 と異なるのは、Stream のような新しい仲介型は導入せず、Iterable/Iterator をそのまま使える点です。
つまり、既存のコレクション(Iterable)も、改造無しにストリーム処理できるのです。
例えば、Xtend で以下のように記述すると、バルク処理ではなく、ストリーム処理になります。このことを少し詳細に見てみましょう。
ids.map[Person::get(it)].filter[sex == MALE].forEach[println(it)]
上記の式のチェーンのつなぎ目の型遷移は、以下のようになっています。
val Iterable<ID>ids = ... val Iterable<Person> people = ids.map[Person::get(it)] val Iterable<Person> men = people.filter[sex == MALE] men.forEach[println(it)]
ここで生じる疑問は2点です。
- Iterable に map や filter というメソッドは無いはずでは?
- Iterable から Iterable への変換時にバルク処理になっているのでは?
1の答え。それは、Extension method です。
Xtend には Extension method があるので、Iterable に対して外からメソッドを追加したように記述できます。具体的には、IterableExtensions によって、これらのメソッドが提供されています。
2の答え。それは、IterableからIterableを作るのに評価は不要ということです。
前段の Iterable を、後段の Iterable でラップして行くのです。最終的な評価は、forEach の中で Iterator 経由で遅延的に行われることになります。これによって、ストリーム処理による遅延評価()が可能になっているわけですね。
ちなみに、この処理の実装として、Xtend の裏で Guava が使われています。
まとめ
Xtend を使うと、今日からすぐに Java プロジェクトに Lambda とストリーム処理を利用できます。Xtend は、型推論や Extension method の導入によって、生の Java よりもプログラムを大幅に簡潔にしてくれます。
にもかかわらず、Xtend は Java の型システム、Java の思考方法を維持しているので、Java プログラマに大きな学習負担を強いることなく、実装効率を向上してくれます。
Java プログラマなら、1日勉強すると、Xtend をだいたい使いこなすことが出来ます。
Xtend は、Java ソースを生成するので、納品 Java プロジェクトにも使いやすいという特徴があります。
これらの特徴を持つ Xtend は、Java プログラマのための Better Java 言語として、とても魅力的ではないでしょうか。
ちなみに、私のチームでは、今では日常的に Xtend で Java プロジェクトを開発/納品しています。皆様も、ぜひ Xtend の Lambda お試しください。