Java9もリリースされたというのにまだJava8がよくわかってないのでまとめてみます。
forEach()
Java8ではList,Map,配列等に forEach()
メソッドが追加されました。
Java7以前は
for(String s : list) {
System.out.println(s);
}
と3行も使っていた処理が
list.forEach(s -> System.out.println(s));
1行で書けるようになりました。
なぜこのように書けるのでしょうか。
関数型インタフェース
Java8では抽象メソッドを1つのみもつインタフェースを関数型インタフェースと呼び、ラムダ式で渡せるようになります
@FunctionalInterface
public interface FunctionalIf {
public String retStr(int number);
}
ただし、以下のメソッドは含まれていてもかまいません
・staticメソッド
・Defaultメソッド
・java.lang.Objectのpublicメソッドのオーバーライド(toString()やequals())
@FunctionalInterface
アノテーションを付けると条件を満たしていないときにコンパイルエラーになりますが、必須ではありません。
ラムダ式
ラムダ式とは、Java8から導入された文法です。
以下の構文で関数型インタフェースを実装することができます
( 実装するメソッドの引数 ) -> { 処理 }
FunctionalIf func = (int num) -> { return num +" * 2 = " + num*2;};
これだけでもかなり簡潔に書けているのですが、さらに省略することができます。
実装するメソッドの引数が1つの場合、型と()を省略可能
FunctionalIf func = num -> { return num +" * 2 = " + num*2;};
引数が1つなので、num
はint型と推論できます。
処理が単文の場合、returnと{}を省略可能
FunctionalIf func = num -> num +" * 2 = " + num*2;
単文なので、文の結果が出力と推論できます。
ラムダ式の利用
最終的にこのような形式になりました
// ラムダ式で関数型インタフェースを実装
FunctionalIf func = num -> num +" * 2 = " + num*2;
// 実装したメソッドを利用
System.out.println(func.retStr(1));
System.out.println(func.retStr(2));
1 * 2 = 2
2 * 2 = 4
ふたたび forEach()
Java8でのforEach()メソッドは、Iterableインタフェースのメソッドdefault void forEach(Consumer<? super T> action)
を実装したクラスで使用できます。
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-
引数のConsumer
はvoid accept(T t)
(何か引数を受け取り、値を返さない)をもつ関数型インタフェースです。
https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/Consumer.html
list.forEach(s -> System.out.println(s));
とは、forEach()メソッドの引数に、Consumer
の実装をラムダ式で渡していたのですね。
ラムダ式利用時の注意点
変数のスコープ
ラムダ式の外側で宣言した変数は、ラムダ式の内側で参照することができます。
ただし、変更することはできません。
String str = " item";
list.forEach(s -> {
System.out.println(s + str); // 参照可能
// str = " changed"; // 変更はNG
});
また、変数の引数名にラムダ式の外側で宣言した変数を使うことはできません(コンパイルエラーになる)
変数名の制限
ラムダ式の引数の変数名に単一の _
は使うことはできません。