【コラム】
Javaのクロージャに関するJSRのドラフトには、クロージャを導入することのメリットとして、新しい制御構文を独自に定義し、JavaのAPIを拡張できるということが挙げられている。前回は、Control invocation syntaxを利用して定義した新しい制御構文の例として、任意のロック機構で排他処理を行うwithLock文を紹介した。
同様の例としてよく挙げられるのが、ストリームの処理に利用するwith文だ。with文では引数にストリームオブジェクトを受け取り、処理が終了した際に必ずそのストリームを閉じるという制御構文である。
具体的には、リスト1のような定義になる(プロトタイプ仕様より引用)。ここでは、withメソッドはjava.io.Closeableオブジェクトを受け取り、それをクロージャのinvoke()に渡すように実装されている。そして処理が終了したらclose()メソッドを実行してストリームを閉じる。前回のwithLockの例と異なるのは、クロージャ自身も引数を受け取るという点だ。
public static R
with(T t, { T ==> R throws E } block) throws E {
try {
return block.invoke(t);
} finally {
try {
t.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
このwith文を利用してストリームを処理している例がリスト2およびリスト3である。最初の例ではBufferedReaderを渡し、最初の1行を読み込む処理を行っている。クロージャ自身も引数としてCloseableオブジェクトを受け取るので、それを":"を用いた文法で指定している点に注意してほしい。次の例ではPrintWriterを渡し、文字を出力している。withの引数はClosableで定義されているので、一般的なストリームやネットワークチャネルはすべて同様に処理することができる。
try {
String text = "";
BufferedReader reader =
new BufferedReader(new FileReader("in.txt"));
with(BufferedReader in : reader) {
text = in.readLine();
}
} catch(IOException ex) {
ex.printStackTrace();
}
try {
PrintWriter writer =
new PrintWriter(new FileWriter("out.txt"));
with(PrintWriter out : writer) {
out.println("Hello!");
}
} catch(IOException ex) {
ex.printStackTrace();
}
ストリームを利用する処理では、最後にclose()メソッドを呼び出すケースが非常に多い。この例のように一度with文を定義しておけば、毎回close()を記述する必要がなくなるため、コードを簡潔にすることができるだろう。
BGGA版の仕様では、メソッドの修飾子として新たに"for"キーワードが使えるようになっている。"for"キーワードは、通常のメソッド修飾子と同様に戻り値の型の前に付けて使用する(※)。
※現行の仕様では識別子の直前に付けるように記述されているが、後に修飾子のように扱うよう変更された。プロトタイプ実装にはこれが反映されているが、仕様には次のアップデートで反映するとのこと。
もしクロージャを利用して繰り返し処理を行う制御構文を定義する場合には、メソッドにfor修飾子を付加する必要がある。前回の記事でUnrestricted closureではbreakやcontinueが使用できると書いたが、正確にはfor修飾子を付けたメソッドに対するUnrestricted closureがその対象となる。すなわち、forで修飾されたメソッドに渡すUnrestricted closureの処理においてcontinueが実行された場合には、処理は次の繰り返しへと映り、breakの場合には制御構文を抜ける。
たとえばリスト4のような例が考えられる。eachIndexは配列をひとつ受け取り、その要素それぞれに対して渡されたクロージャの処理を実行する。
public static for void
eachIndex(T[] array, { T ==> void } block) {
for (T t : array) {
block.invoke(t);
}
}
呼び出し側はリスト5のようになる。識別子の前に"dor"キーワードを付ける点が通常の呼び出しと異なる。処理は文字列配列の要素を表示するという単純なものだが、もしそれが"NOTHING"だった場合はcontinueで無視するようになっている。eachIndexはforで修飾されているため、このcontuinueによって処理は次の要素へと移されることになる。もしbreakならばeachIndexの処理を終了し、returnならばエンクロージャの処理そのものを終了することになる。
String[] fruits = {"apple", "orange", "NOTHING", "blueberry"};
for eachIndex(String item : fruits) {
if(item.equals("NOTHING")) continue;
System.out.println(item);
}
上記は配列に対する例だが、同様の構文をjava.util.Mapに対して定義する場合、リスト6のように記述できる(プロトタイプ仕様より引用)。これは渡されたMapオブジェクトの各エントリをそれぞれ1つずつ処理するというもの。クロージャには各エントリのキーと値が渡される。この際、getKey()およびgetValue()の実行時にIlligalStateExceptionがスローされる可能性があるため、eachEntryの定義にはthrows宣言が含まれる。
public static for void
eachEntry(Map map, { K, V ==> void throws X } block)
throws X {
for (Map.Entry entry : map.entrySet()) {
block.invoke(entry.getKey(), entry.getValue());
}
}
呼び出し側はリスト7のようなコードになる。クロージャに渡されるのはMapオブジェクトのキーと値なので、eachEntryの呼び出し時にそれぞれの型を指定している。
Map map = new HashMap();
map.put("java.lang", 43);
map.put("java.io", 63);
map.put("org.xml.sax", 13);
map.put("javax.sql", 17);
for eachEntry(String name, Integer value : map) {
if (name.startsWith("org.")) continue;
System.out.println(name + ":" + value);
}
J2SE 5.0のリリース時に、for-each風に利用できるfor文のための新しい構文が導入された。for修飾子を用いることで、繰り返し処理のための同様のシンタックスシュガーが独自に定義できるというわけだ。
Java SE 7ではクロージャも含めていくつかの画期的な機能の導入が予定されている。それらを使いこなせるようになれば開発者にとって非常に強力な武器になるだろう。しかし残念ながら、すぐ使えと言われて使いこなせるほど単純なものでもない。J2SE 5.0で導入されたジェネリクスにしても、未だに戸惑ってしまうユーザーは少なくないだろう。
そのように新機能で挫折しないためには、リリース前から情報を集めてある程度の予備知識を蓄えておくことが重要だ。幸いにして現在のJavaはオープンな開発体制が敷かれており、リリース前でも大半の情報は手に入れることができる。参照実装も早い段階で公開される。実際に使ってみるまでいかなくても、その概要を知っておくだけでリリース後のモチベーションが大きく違ってくるはず。本連載で6回に渡って行ってきた"クロージャ特集"もその手助けになれば幸いである。
提供:マイナビ
大学・大学院・短大・専門学生向けの就職情報サイト「マイナビ2010」「マイナビ2009」に今すぐ登録しよう! 大手企業からベンチャー企業までの約13,000社の企業情報を公開、エントリーが可能です。2010年卒予定の方は「マイナビ2010」に、2009年卒予定の方は「マイナビ2009」に登録してください。
毎日コミュニケーションズはプライバシーマークを取得しています。
| ソーシャル・パーティー・ロボットバンド「Z-MACHINES」がデビュー決定 [12:41 5/21] |
| 【レポート】早大と防衛医大、外科手術に革命を起こす「ナノ絆創膏」を開発 [10:00 5/21] |
| JAEA、銅やアルミニウムなど非レアメタルで磁気の流れを生みだす原理を発見 [10:00 5/21] |
| 九大と住友金属鉱山、レアメタル分離用の新規抽出剤を開発 [09:30 5/21] |
| Microchip、Arduino互換のchipKIT Fubarino Mini boardなどを発表 [09:30 5/21] |
|
『ジョジョの奇妙な冒険』×コンバース「ALL STAR」の予約受付スタート! [13:25 5/21] ホビー |
|
ストーム、デスクトップ向け最新APU"A4-4000"搭載ミニタワーを約3万円から [13:21 5/21] パソコン |
|
コンピュウェア、強化されたAPMaaS型ユーザー体感管理ソリューション [13:17 5/21] エンタープライズ |
|
HTML5やWebGLを利用してWebブラウザでマンガ作成「箱人形マンガ」 [13:07 5/21] パソコン |
|
千葉県市川市、京成八幡駅&都営新宿線本八幡駅の連絡通路が供用開始に [13:05 5/21] 旅行 |