【コラム】

Java API、使ってますか?

58 Java SE 7の要注目機能"クロージャ"はどうなるのか その6

58/60

新しい制御構文の定義例

Javaのクロージャに関するJSRのドラフトには、クロージャを導入することのメリットとして、新しい制御構文を独自に定義し、JavaのAPIを拡張できるということが挙げられている。前回は、Control invocation syntaxを利用して定義した新しい制御構文の例として、任意のロック機構で排他処理を行うwithLock文を紹介した。

同様の例としてよく挙げられるのが、ストリームの処理に利用するwith文だ。with文では引数にストリームオブジェクトを受け取り、処理が終了した際に必ずそのストリームを閉じるという制御構文である。

具体的には、リスト1のような定義になる(プロトタイプ仕様より引用)。ここでは、withメソッドはjava.io.Closeableオブジェクトを受け取り、それをクロージャのinvoke()に渡すように実装されている。そして処理が終了したらclose()メソッドを実行してストリームを閉じる。前回のwithLockの例と異なるのは、クロージャ自身も引数を受け取るという点だ。

リスト1 with文の定義例

    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で定義されているので、一般的なストリームやネットワークチャネルはすべて同様に処理することができる。

リスト2 withを使ったストリームの処理

        try {
            String text = "";
            BufferedReader reader = 
                new BufferedReader(new FileReader("in.txt"));

            with(BufferedReader in : reader) {
                text = in.readLine();
            }
        } catch(IOException ex) {
            ex.printStackTrace();
        }

リスト3 withを使ったストリームの処理 その2

        try {
            PrintWriter writer = 
                new PrintWriter(new FileWriter("out.txt"));

            with(PrintWriter out : writer) {
                out.println("Hello!");
            }
         } catch(IOException ex) {
            ex.printStackTrace();
         }

ストリームを利用する処理では、最後にclose()メソッドを呼び出すケースが非常に多い。この例のように一度with文を定義しておけば、毎回close()を記述する必要がなくなるため、コードを簡潔にすることができるだろう。

"for"修飾子を用いた繰り返し構文の定義

BGGA版の仕様では、メソッドの修飾子として新たに"for"キーワードが使えるようになっている。"for"キーワードは、通常のメソッド修飾子と同様に戻り値の型の前に付けて使用する(※)。

※現行の仕様では識別子の直前に付けるように記述されているが、後に修飾子のように扱うよう変更された。プロトタイプ実装にはこれが反映されているが、仕様には次のアップデートで反映するとのこと。

もしクロージャを利用して繰り返し処理を行う制御構文を定義する場合には、メソッドにfor修飾子を付加する必要がある。前回の記事でUnrestricted closureではbreakやcontinueが使用できると書いたが、正確にはfor修飾子を付けたメソッドに対するUnrestricted closureがその対象となる。すなわち、forで修飾されたメソッドに渡すUnrestricted closureの処理においてcontinueが実行された場合には、処理は次の繰り返しへと映り、breakの場合には制御構文を抜ける。

たとえばリスト4のような例が考えられる。eachIndexは配列をひとつ受け取り、その要素それぞれに対して渡されたクロージャの処理を実行する。

リスト4 新しい繰り返し構文の定義例

    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ならばエンクロージャの処理そのものを終了することになる。

リスト5 eachIndex文の利用例

       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宣言が含まれる。

リスト6 Mapに対する繰り返し構文の定義例

    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の呼び出し時にそれぞれの型を指定している。

リスト7 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回に渡って行ってきた"クロージャ特集"もその手助けになれば幸いである。

58/60

インデックス

連載目次
第60回 どうなる? 今後のJavaプラットフォーム(Java SE編)
第59回 どうなる? 今後のJavaプラットフォーム(Java EE編)
第58回 Java SE 7の要注目機能"クロージャ"はどうなるのか その6
第57回 Java SE 7の要注目機能"クロージャ"はどうなるのか その5
第56回 Java SE 7の要注目機能"クロージャ"はどうなるのか その4
第55回 Java SE 7の要注目機能"クロージャ"はどうなるのか その3
第54回 Java SE 7の要注目機能"クロージャ"はどうなるのか その2
第53回 Java SE 7の要注目機能"クロージャ"はどうなるのか
第52回 Early Draftが公開されたJSF 2.0
第51回 EJBから独立したJava Persistence 2.0
第50回 モバイルJavaの新しい潮流となるか - MSA 2.0のドラフト公開
第49回 やっぱり基本はServlet - Servlet 3.0のEarly Draftを読む
第48回 JOGLで3Dプログラミング その4
第47回 JOGLで3Dプログラミング その3
第46回 JOGLで3Dプログラミング その2
第45回 JOGLで3Dプログラミング
第44回 JARファイルを効率的にネットワーク転送するためのPack200形式
第43回 Early Draftで把握するEJB 3.1の新機能
第42回 次世代の携帯端末向けJava仕様"MIDP 3.0"はどうなるか その2
第41回 次世代の携帯端末向けJava仕様"MIDP 3.0"はどうなるか その1
第40回 リソースアダプタによる接続の仕組み
第39回 JCAを利用したシステム間接続
第38回 Java EEと外部システムの接続性を支えるJCAがバージョンアップ
第37回 Javaのモジュラリティ強化を担う"スーパーパッケージ"とは
第36回 JSR 308対応のコンパイラを試す
第35回 公開されたJSR 308のEarly Draftを検証する
第34回 スクリプト言語とJavaを結びつけるJSR 223
第33回 Java EE環境に統一されたコンポーネントモデルを提供するJSR 299 その2
第32回 Java EE環境に統一されたコンポーネントモデルを提供するJSR 299 その1
第31回 Javaの文法がそのまま使えるスクリプト言語"BeanShell"
第30回 Javaアプリケーションにオブジェクトのキャッシュ機構を提供するJCache API
第29回 Javaアプリケーションからのリソース管理を可能にするJSR 284
第28回 XMLデータソースへの問い合わせはJSR 225で
第27回 Portlet Specification 2.0をもっと手軽に利用する
第26回 次期Javaポートレット仕様となるJSR 286
第25回 JSFとポートレットをつなげるJSR 301
第24回 Webサービス向けのポートレット仕様「WSRP」
第23回 高い相互運用性を実現するポートレットAPI - JSR 168
第22回 Java EE環境でタスクのスケジューリングを可能にするJSR 236
第21回 Java EE環境でのスレッドプログラミングを可能にするJSR 237
第20回 音声認識/合成のためのAPI - Java Speech APIとJSR 113
第19回 JSR 291でJavaプラットフォームにダイナミックコンポーネントモデルを導入
第18回 JAX-RSで簡単RESTful - JSR 311
第17回 待望のServlet 3.0がJSRに登場 - JSR 315
第16回 アノテーションを使ってバグ退治 - JSR 305
第15回 アノテーションをさらに広い範囲で利用可能にするJSR 308
第14回 Webアプリケーション開発の要となるか - JSF 2.0がJSRに登場
第13回 Webサービス経由でのJMX Agentへの接続を可能にするJSR 262
第12回 Javaアプリケーションのモジュール化をサポートするJava Module System
第11回 "NIO.2"がやってきた - JSR 203: More New I/O APIs for the Java Platform
第10回 JSR 295: Beans Bindingの参照実装を試す
第9回 けっこう便利! 単位を扱うAPI -- JSR 275: Units Specification
第8回 アノテーションでバリデーション - JSR 303: Bean Validator
第7回 Swing開発の救世主となるか - Swing Application Framework
第6回 JavaBeansのプロパティを同期させるバインディングAPI
第5回 誰よりも早く"Java SE 7"を睨む
第4回 日時情報の取り扱いを改善する JSR 310: Date and Time API
第3回 古いAPIも進化している!? - JSR 919: JavaMail 1.4
第2回 JSR 1 リアルタイムJava仕様
第1回 JCPによって進められるJava関連技術の標準化

もっと見る

提供:マイナビ

会員登録はこちら

大学・大学院・短大・専門学生向けの就職情報サイト「マイナビ2010」「マイナビ2009」に今すぐ登録しよう!  大手企業からベンチャー企業までの約13,000社の企業情報を公開、エントリーが可能です。2010年卒予定の方は「マイナビ2010」に、2009年卒予定の方は「マイナビ2009」に登録してください。

毎日コミュニケーションズはプライバシーマークを取得しています。

関連キーワード


人気記事

一覧

イチオシ記事

新着記事