【コラム】

Java API、使ってますか?

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

55/60

クロージャからローカル変数へのアクセス

今回も第54回に引き続いてJava SE 7で導入される予定のクロージャについて紹介する。従来のJavaでも、無名インナークラスを使えばクロージャに近いコードを記述することはできた。その無名インナークラスとクロージャの非常に大きな違いが、クロージャではそれが定義された環境(エンクロージャ)のスコープ内のローカル変数にアクセスできる点だ。

リスト1で定義している{ int y => x += y }というクロージャは、エンクロージャ内のローカル変数xに、引数で渡された値yを加算して返すというものだ。クロージャ内で加算されたxの値は、クロージャの外側でも有効である。また、エンクロージャ側でxの値を変更した場合にも、次にクロージャが呼び出された際にはそれが反映される。したがってこのプログラムの実行結果はプロンプト1のようになる。

現在のところ仕様には記載されていないが、クロージャ側から参照されるローカル変数には@Sharedアノテーションを付加することになるようだ。@Sharedが指定されていない場合にはコンパイル時に警告が発せられる。

リスト1 AccessLocalVariable.java

class AccessLocalVariable {
    public static void main(String[] args) {
        @Shared int x = 0;

        { int => int } add = { int y => x += y };

        System.out.println(add.invoke(5));
        System.out.println(add.invoke(5));
        ++x;
        System.out.println(add.invoke(5));
  }
}

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

>java AccessLocalVariable
5
10
16

クロージャのインタフェース型への変換

前回、クロージャはインタフェースの一種として扱われると書いたが、それに関連した機能としてクロージャリテラルはインタフェース型の変数に代入することができる。条件はそのインタフェースがただ1つのメソッドを持っており、その引数および戻り値の型/数がクロージャのそれと一致することだ。

たとえばリスト2のようなインタフェースがあるとする。IntFunctionはint型の値を2つ受け取り、int型の値を1つ返すようなメソッドadd()をただ1つもつ。

リスト2 IntFunction.java

interface IntFunction {
    int add(int x, int y);
}

通常であればこのIntFunctionをimplementsしたクラスを作成し、add()を実装して利用するという手順になるが、クロージャを利用した場合リスト3のように記述することができる。IntFunction型の変数plusには、IntFunctionのインスタンスの代わりにadd()と同じ引数/戻り値を持つクロージャリテラルを代入している。その上でplusに対してadd()メソッドを呼び出せば、代入したクロージャが実行される。

リスト3 ConversionSample.java

class ConversionSample {
    public static void main(String[] args) {
        IntFunction plus = { int x, int y => x + y };

        int res = plus.add(10, 20);
        System.out.println(res);
    }
}

もしIntFunctionがint型の値を2つ受け取りint型の値を1つ返すメソッド(たとえばint sub(int x, int y)のような)をもうひとつ持っている場合にはこの変換は使えない。

この{int x, int y => x + y}というクロージャは、実装上はIntFunctionの無名実装クラスとして扱われるとのこと。ConversionSample.classを逆コンパイルしてみるとリスト4のようになっており、IntFunctionインタフェースのadd()メソッドの代わってinvoke()メソッドの実装が用意されていることがわかる。

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

import java.io.PrintStream;

class ConversionSample {
    ConversionSample() { }

    public static void main(String args[]) {
        Object obj = new Object() {
        IntFunction plus$1;
        };
        obj._fld1 = _2B_INSTANCE0;
        int i = ((_cls1) (obj))._fld1.invoke(10, 20);
        System.out.println(i);
    }

    public static final _cls2 _2B_INSTANCE0 = new IntFunction() { 
        public final int _2B_invoke(int i, int j) {
            return i + j;
        }

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

この変換が可能なのはインタフェースがただひとつのメソッドを持つときと書いたが、例外的に複数のメソッドを持つインタフェースでも適用できることがある。それは、複数のメソッドを持つインタフェースで、ひとつのメソッド以外のメソッドの引数がすべてObject型である場合。このときクロージャは、Object以外の型を持つメソッドに適用される。

この条件に当てはまるインタフェースの例としてはjava.util.Comparator<T>が挙げられている。Comparatorには「int compare(T o1, T o2)」と「boolean equals(Object obj)」という2つのメソッドが宣言されている。したがってcompare()メソッドの方にクロージャが適用できる。

たとえば次のような例が考えられる。java.util.Arraysクラスのsort()メソッドは第1引数にクラスTの配列を、第2引数にComparatorのインスタンスを取る。Comparatorインタフェースには順序付けの条件を定めるためのメソッドとして「int compare(T o1, T o2)」が宣言されている。ここで、Comparatorをimplementsする代わりに、クロージャを用いてcompare()メソッドの実装を定義することで順序付けの条件を指定しているのがだ。

このクロージャは2つの文字列を受け取り、最初の文字列の方が長ければ正の値、短ければ負の値、長さが等しい場合にはcompareTo()で比べた結果を返す。ここではこのクロージャをArrays.sort()の第2引数に渡しているので、ソート後のwordsは長さの短い文字列順に並ぶことになる ()。

リスト5 onversionSample2.java

import java.util.Arrays;

class ConversionSample2 {
    public static void main(String[] args) {
        String[] words = { "aaaa", "bbb", "cc", "ddddd" };

        // Comparatorの実装クラスの代わりにクロージャを渡す
        Arrays.sort(words,
            { String s1, String s2 =>
                 int r = s1.length() - s2.length();
                 (r == 0) ? s1.compareTo(s2) : r 
            });

        for (String word : words) {
            System.out.println(word);
        }
    }
}

プロンプト2 ConversionSample2.javaの実行結果

> java ConversionSample2
cc
bbb
aaaa
ddddd

この機能をうまく使えばインタフェースの実装をシンプルに記述できるようになるだろう。

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

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

関連キーワード


人気記事

一覧

イチオシ記事

新着記事