kmizuの日記

プログラミングや形式言語に関係のあることを書いたり書かなかったり。

Java 7のクロージャ(BGGA版)のプロトタイプを試してみた(3) - closure conversion

参考URLはこの辺:http://tronicek.blogspot.com/2007/12/closure-conversion.html

BGGAのクロージャには、クロージャを互換性のあるインタフェース型に自動的に変換するclosure conversionという機能がある。ここで、互換性のあるインタフェース型というのは、

  • メソッドを一つだけ持っており
  • そのメソッドの返り値の型および引数の型がクロージャの型と互換性がある

という条件を満たしていれば良いようだ。

たとえば、

interface F {
  int apply(int arg);
}

というインタフェースがあったとすると、

F pow2 = {int arg => arg * arg};

という風に書くことができる。実際に以下のコードを書いて、

public class ClosureConversions1 {
  interface F { int apply(int arg); }
  public static void main(String[] args) {
    F pow2 = {int arg => arg * arg};
    System.out.printf("pow2(%d) = %d%n", 3, pow2.apply(3));
  }
}

実行してみると、

>java ClosureConversions1
pow2(3) = 9

のようになり、意図通りにクロージャがF型のインスタンスに変換されていることがわかる。

既存のJavaライブラリでは、しばしばメソッドを1つだけ持ったインタフェース型がfunction typeの代替のような感じで使われているため、closure conversionを使えば、そのような既存のライブラリを呼び出すときに、無名クラスを使うのに比べてより簡潔に記述することができる。

たとえば、java.ioパッケージには、FileFilterというインタフェースがあって、この型のオブジェクトをjava.io.File#listFiles(FileFilter filter)メソッドに引数として渡すことで、ディレクトリからファイルの一覧を取得するときに、ディレクトリの全要素を取得するのではなく、条件を満たした要素のみを取得するようにできるが、ここで、インタフェースFileFilterは

boolean accept(File pathname) 

というたった一つのメソッドを持っているため、closure conversionを使うことができる。

というわけで、java.io.File#listFiles(FileFilter filter)メソッドの呼び出しを、closure conversionを利用して書いたのが、以下のコード。

import java.io.*;

public class ClosureConversions2 {
	public static void main(String[] args) {
		File[] files = new File(".").listFiles(
		  {File file => file.getName().endsWith(".java")}
		);
		for(File f:files) System.out.println(f.getName());
	}
}

実行結果。

>java ClosureConversions2
ClosureConversions1.java
ClosureConversions2.java