いまさらながら、Javaのラムダ式についての解説を書きます。
Javaのラムダ式
そもそもラムダ式は無名関数を記述する方法として、関数型言語でよく使われていたものです。
関数に関数を渡すときなど、一時的な関数を作るときに使います。
Javaにおいては関数というものが存在しません(メソッドは必ずクラスの中に定義される)。
そのため、Javaのラムダ式は「単一の抽象メソッドを持つインターフェースのインスタンスを生成して受け渡す」という挙動になります。
単一のメソッドに限定されるのは、ラムダ式では1つの式しか記述できないため、複数のメソッドを持つインターフェースだとどのメソッドの式かが定まらないためです。
記述の方法としては
(引数) -> {処理}
と書きます。
引数や処理が1つしかない場合、カッコは省略できます。
具体例としてCollections
のsort
メソッドを見ていきます。
これは第1引数のList
に対してソートを実行するメソッドですが、第2引数にComparator
インターフェースのインスタンスを渡すとcomparable
メソッドを使ってソート順を判断してくれます。
例えば、文字列のリスト(strList
)を文字の長さでソートしたいときは無名関数だと以下のように記述できます。
Collections.sort(strList, new Comparator(){
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
})
Comparator
クラスはcompare
メソッドだけが定義されているインターフェースなのでラムダ式で以下のように書き換えることができます。
List<String> strList = Arrays.asList("a","ccc","bb");
Collections.sort(strList, (s1, s2) -> s1.length() - s2.length());
System.out.println(strList); // [a, bb, ccc]
関数型インターフェースとメソッド参照
Comparator
クラスにはcomparing
メソッドが以下のように定義されています。
comparing(Function<? super T,? extends U> keyExtractor);
引数の型がFunction<? super T,? extends U>
となっていますが、これは関数型インターフェースの一種です。
関数型インターフェースはラムダ式が使えるインターフェースであり、様々な種類が用意されています。
今回のFunction<? super T,? extends U>
は引数がT、戻り値がUの型を持つインスタンスであることを示しています。
TとUについては、以下のように定義されています。
<T,U extends Comparable<? super U>> Comparator<T>
Tは比較対象のインスタンスの型。Uは比較するためのフィールドの型です。
そのため、この関数型インターフェースはTのインスタンス同士を比較するための情報Uを返す式であるという意味になります。
先ほどのCollections.sort
の処理をcomparing
メソッドで書くと以下のようになります。
Collections.sort(strList, comparing(s -> s.length()));
s -> s.length()
の部分は、String型インスタンスsのlengthメソッドを呼び出すという意味なので、メソッド参照で書き換えることができます。
メソッド参照はあるインスタンスに対して、そのクラスのメソッドを呼び出すという処理を簡単に書ける記述方法です。
クラス名::メソッド名
で記述します。
先ほどの例をメソッド参照で書くと以下のようになります。
Collections.sort(strList, comparing(String::length));
参考
(o1, o2) -> o1 - o2 なんて呪文はもうやめて! - Java8でのComparatorの使い方
JavaDoc:Comparator