Java8 のラムダ式について勉強したこと


最近ようやくJavaSE 1.8 で追加された新機能であるラムダ式について勉強したので、そのメモを残しておこうと思います。
ラムダ式を理解するには「関数型インターフェース」について知っておくべきなので、まずはそちらから。


## 関数型インターフェース
関数型インターフェースとは、単一の抽象メソッドを持つインターフェースのことです。
例えば、[java.lang.Comparator](http://docs.oracle.com/javase/jp/8/docs/api/java/util/Comparator.html) は関数型インターフェースです。
(抽象メソッドのところにcompare(T o1, T o2)、equals(Object obj) の二つありますが、equals の方はObject#equals メソッドのオーバーライドであり、compare のみがComparator インターフェイースの持つ抽象メソッドと考えられるみたいです。）  

また、Java 8 では、アノテーション`@FunctionalInterface`を書くことで、関数型インターフェイスであることを明示することができるようになりました。  

以下ではこのComparator インターフェースを実装したクラスの例を考え、ラムダ式を使ってみたいと思います。  

## ラムダ式
ラムダ式とは、簡単に書くと「関数型インターフェイスのメソッドの実装を表現できる式」です。
`(コードブロックに渡される変数を宣言したリスト) -> {処理ブロック}`
といった形式で表されます。  

これまではメソッドの引数にコードブロックを入れる場合、
`コードブロックをクラスに　-> クラスをオブジェクトに -> そのオブジェクトの参照値をメソッドの引数に`
といった処理が必要でしたが、Java8からは処理の塊をラムダ式としてそのままメソッドの引数に入れることができるようになりました。  
Stringオブジェクトを長さ順にソートするという以下のプログラムで見てみます。
ラムダ式を使わない場合、以下のようなコードが書けると思います。  

```NonLambda.java
import java.util.*;
public class LambdaTest {
    public static void main (String[] args) {
        ArrayList<String> array = new ArrayList<String>();
        array.add("sato");
        array.add("suzuki");
        array.add("takahashi");
        array.add("tanaka");
        array.add("ito");

        System.out.print("ソート前: ");
        for (String s : array){
            System.out.print(s + " ");
        }
        System.out.println();

        StringComparator strComp = new StringComparator();
        Collections.sort(array, strComp);

        System.out.print("ソート後: ");
        for (String s : array){
            System.out.print(s + " ");
        }
        System.out.println();
    }
}

class StringComparator implements Comparator<String> {
    @Override
    public int compare(String s1, String s2){
        return Integer.compare(s1.length(), s2.length());
    }
}
```


上記例ではArrayList に保存した文字列を、長さが短い順にソートしています。
長さが短い順に、という条件をCollections.sort() に渡すために、その処理を行うクラスComparatorインターフェースを実装し作成、そのクラスのインスタンスを生成し引数に渡す、といったことを行っています。
ここで、StringComparator クラスのcompare メソッドでは、引数としてStringオブジェクトを受け取り比較するという処理を行っています。  
これをラムダ式を使って表すと、以下のようになります。  


```LambdaTest.java
import java.util.*;
public class LambdaTest {
    public static void main (String[] args) {
        ArrayList<String> array = new ArrayList<String>();
        array.add("sato");
        array.add("suzuki");
        array.add("takahashi");
        array.add("tanaka");
        array.add("ito");

        System.out.print("ソート前: ");
        for (String s : array){
            System.out.print(s + " ");
        }
        System.out.println();

        Collections.sort(array, (String s1, String s2) -> Integer.compare(s1.length(), s2.length()));

        System.out.print("ソート後: ");
        for (String s : array){
            System.out.print(s + " ");
        }
        System.out.println();
    }
}

```

このように、Comparator を実装したクラスのインスタンスを生成することなく、直接メソッドの引数に処理を渡すことができます。  
また、`(コードブロックに渡される変数を宣言したリスト) -> {処理ブロック}`の引数リストでは、型が推定可能なものについては型宣言を省略することができるそうです。
つまり、上の例では、
`Collections.sort(array, (String s1, String s2) -> Integer.compare(s1.length(), s2.length()));`
を
`Collections.sort(array, (s1, s2) -> Integer.compare(s1.length(), s2.length()));`
と書いても問題ありません。  

といった風に、ラムダ式を用いることでコード量が削減できました。
上の例では`(コードブロックに渡される変数を宣言したリスト) -> {処理ブロック}`の処理ブロック部分が1行だったため、`{}`無しでアロー演算子に続けて書いていますが、長くなる場合は以下のように通常のメソッドのように書き、return文により返り値を設定します。  

```
(s1, s2) -> {
    if (s1.length() > s2.length()) {
        return -1;
    } else {
        return 1;
    }
}
```

## おわりに

* 関数型インターフェースを使う場合にラムダ式が書ける  
* コード量が大きく削減できる  
* 引数と処理を`->`をつなぐ

こんなところがラムダ式に関する簡単なまとめとなります。
知らないとコード見ても？？って感じになるので、とりあえず何が起こってるかは分かっておきたいですね。