Xtendを触ってみる その6 クロージャ

今回はXtendに標準で用意されているライブラリを紹介しようと思っていたのですが、その前にクロージャについて触れていなかったので先にクロージャについて書くことにします。
構文はこんな感じです。

[ 引数のリスト | 処理 ]

たとえばJavaでこんな感じで書いていた処理が…

List<String> names = new ArrayList<String>();
for (String name : persons) {
  names.add(name);
}

mapメソッドクロージャを使って以下のように記述できます。他の言語でクロージャを使ったことがあれば特に難しいことはないでしょう。

persons.map( p | p.name )

なお、クロージャはJava8のクロージャと同じくjava.util.Comparatorなどのようにメソッドが1つのインターフェースに自動変換することができます。たとえばこんな感じ。sortメソッドはXtendの組み込みライブラリによってjava.util.Iterableに追加された拡張メソッドで、引数にComparatorを取り、ソート後のListを返します。

val sortedList = persons.sort(a, b | a.name.compareTo(b.name))

実際に生成されたJavaコードは以下のようになっています。クロージャを直接インターフェースに変換しているのではなく、クロージャはFunctionオブジェクトとして作成し、インターフェースからそのクロージャを呼び出しています。

final Function2<Person,Person,Integer> _function = new Function2<Person,Person,Integer>() {
    public Integer apply(final Person a , final Person b) {
      int _compareTo = a.name.compareTo(b.name);
      return _compareTo;
    }
  };
List<Person> _sort = IterableExtensions.<Person>sort(persons, new Comparator<Person>() {
    public int compare(Person o1,Person o2) {
      return _function.apply(o1,o2);
    }
});
final List<Person> sortedList = _sort;

作成したクロージャを自分で実行してみます。クロージャを呼び出すにはapplyメソッドを使います。

val func = [ String s | println(s) ]
func.apply("Hello World!")

もちろんメソッドの引数にクロージャを取ったり、クロージャを返すメソッドを作ることもできます。クロージャの型は以下のように記述します。Scalaと同じですね。

(引数の型のリスト) => 戻り値の型

実際に引数にクロージャを取るメソッドと戻り値としてクロージャを返すメソッドを作ってみました。

// クロージャを引数に取るメソッド
def takeClosure(String name, (String) => String function){
  function.apply(name)
}

// 戻り値としてクロージャを返すメソッド
def (String) => String returnClosure(){
  [ String s | "Hello " + s + "!" ]
}

次回こそXtendの標準ライブラリの紹介を…と思ったのですが、その前に拡張メソッド第二弾ということで、ユニットテストなどで拡張メソッドの実装を差し替えたいという場合に対処する方法について触れようと思います。というわけで続きます。