【コラム】

Java API、使ってますか?

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

54/60

クロージャの基本形

前回はJava SE 7で導入される予定のクロージャについて、その概要とプロトタイプ実装の使い方を紹介した。今回は、実際にクロージャを用いたプログラムを書いてみたい。

BGGA版の仕様では、クロージャを使うための基本となる演算子は「=>」となっている。クロージャを定義する基本的な形は、この演算子と{}を組み合わせて次にようになる。

{パラメータのリスト => ブロックステートメント}

クロージャを実行するにはinvoke()メソッドを呼び出す。たとえばリスト1は「1234」という数値を返すクロージャを定義し、invoke()を使って実行した例である。「{=>1234}」の部分がクロージャを表すブロックだ。

リスト1 FirstClosure.java - 最も基本的なクロージャの例

public class FirstClosure {
    public static void main(String[] args) {
        int answer = { => 1234 }.invoke();
        System.out.println(answer);
    }
}

パラメータを渡す場合にはリスト2のように「=>」の前にパラメータの型と変数名を宣言する。そしてinvoke()の呼び出しの部分で、通常のメソッドの場合と同様に実際に渡す値を指定する。

リスト2 クロージャにパラメータを渡す

        int answer = { int x => x * x }.invoke(10);
        System.out.println(answer);

2つ以上のパラメータを渡したい場合には、リスト3のようにコンマで続けて指定すればよい。

リスト3 2つ以上のパラメータはコンマで続けて指定

        int answer = { int x, int y => x + y }.invoke(1357, 8642);
        System.out.println(answer);

さて、プロトタイプのコンパイラでは、このクロージャはインタフェースのインスタンスとして実装されている。実際にコンパイルしてみると(コンパイル方法は第53回を参照)、javax.lang.function.Iやjavax.lang.function.IIIといったインタフェースが自動的に生成されるはずだ。これらはinvoke()というメソッドをひとつだけ持ったインタフェースとして定義されている(ただし実際に中身が定義されたインタフェースはjavax.lang.function.unrestrictedパッケージにある。プロトタイプであるため、細かな部分の扱い方はまだ決定されていない)。

たとえば前述のClosureWith2Param.javaをコンパイルした場合、javax/lang/functionディレクトリ(およびjavax/lang/function/unrestrictedディレクトリ)にIII.classというファイルが生成されるはずだ。ためしにunrestrictedの方のクラスをJadなどのツールを使って逆コンパイルしてみると、リスト4のような内容になっている。

リスト4 III.classを逆コンパイル

public interface III
{
    public abstract int invoke(int i, int j) throws Throwable;
}

一方、ClosureWith2Param.classの方を逆コンパイルしてみるとリスト5のようになる。これを見ると、クロージャの部分はjavax.lang.function.IIIインタフェースをimplementsした_cls1というstaticクラスになっていることがわかる。当然、invoke()メソッドの実装がクロージャのinvoke()メソッドを呼び出した際に実行される内容だ。この_cls1のインスタンスがClosureWith2Paramのフィールドとして用意されて、それを介してinvoke()メソッドが実行されている。

リスト5 ClosureWith2Param.classを逆コンパイル

import java.io.PrintStream;
import javax.lang.function.III;

public class ClosureWith2Param {
    public ClosureWith2Param() {}

    public static void main(String args[]) {
        static class _cls1 implements III {
            public final int _2B_invoke(int j, int k) {
                return j + k;
            }

            public final int invoke(int j, int k) {
                return _2B_invoke(j, k);
            }
        }

        int i = _2B_INSTANCE0.invoke(1357, 8642);
        System.out.println(i);
    }

    public static final _cls1 _2B_INSTANCE0 = new _cls1();
}

関数型の利用

クロージャとあわせて関数型も利用できるようになる。関数型はクロージャに対する参照を代入するために使用する型で、クロージャと同様に=>と{}を組みわ合せで次のような形で記述する。

{ パラメータの型のリスト => 戻り値の型}

たとえば前述の3つのサンプル (リスト1、リスト2、リスト3) を、関数型を使って一度変数に代入してから実行するように書き換えるとリスト6のようになる。

リスト6

        { => int } number = { => 1234 };
        System.out.println(number.invoke());

        { int => int } square = { int x => x * x };
        System.out.println(square.invoke(10));

        { int, int => int } plus = { int x, int y => x + y };
        System.out.println(plus.invoke(1357, 8642));

関数型はメソッドのパラメータや戻り値の型としても指定することができる。指定する方法は通常のクラスの場合と同様。たおえば文字列を1つ受け取り、戻り値のない関数型は{String => void}のように表すことができる。これをメソッドのパラメータとして用いた例がリスト7のsayHello()メソッドである。

リスト7 FunctionTypeAsParam.java - メソッドのパラメータに関数型を指定

public class FunctionTypeAsParam {
  static void sayHello(String[] persons, 
                       {String => void } block) {
    for (String person: persons) {
      block.invoke(person);
    }
  }

  public static void main(String[] args) {
    String[] persons = {"Taro", "Keiko"};
      sayHello(persons, 
          {String p => System.out.println("Hello " + p + "."); });
      sayHello(persons, 
          {String p => System.out.println("Good morning " + p + "."); });
  }
}

これを実行するとsayHello()は渡された文字列配列のそれぞれの要素をクロージャのinvoke()に渡すという内容になっている。sayHello()を呼び出す際に渡すクロージャを変えれば、実行されるコードも変わる。この例の場合、1回目の呼び出しではパラメータの文字列に"Hello "を付けて標準出力に出力するという内容のクロージャを渡しており、2回目では"Good morning "を付けて出力するクロージャを渡している。これを実行するとプロンプト1のように出力される。

プロンプト1 FunctionTypeAsParam.javaの実行結果

> java FunctionTypeAsParam
Hello Taro.
Hello Keiko.
Good morning Taro.
Good morning Keiko.

リスト8はClosures for the Java Programming Languageに掲載されている関数型をメソッドの戻り値に指定した例である。この例では、makeCondition()メソッドが{ => boolean }という関数型を返すように宣言されており、実際には{ => Math.random() < 0.5 }というクロージャを返している。すなわち、makeCondition()メソッドの戻り値はランダムでtrueかfalseを返すクロージャとなる。

リスト8 FunctionTypeAsReturnType.java - メソッドの戻り値に関数型を指定

public class FunctionTypeAsReturnType {
  static { => boolean } makeCondition() {
    return { => Math.random() < 0.5 };
  }

  public static void main(String[] args) {
    { => boolean } condition = makeCondition();
    for (int c=1; condition.invoke(); c++) {
      System.out.println("trying " + c + " times...");
    }
  }
}

なんとなくクロージャを含んだJavaプログラムが一体どういうものになるのかわかっていただけただろうか。「=>」という演算子にはまだ強い違和感を覚えるが、普及すればジェネリクスのように自然に読めるものになっていくだろうと期待したい。

54/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」に登録してください。

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

関連キーワード


人気記事

一覧

イチオシ記事

新着記事