今更ですがJava8らしいコードでプログラミングしたいなーと思ったのでさわりの部分軽くまとめてみます。
Java8の主な新機能
- ラムダ式
- メソッド参照
- Stream API
今回はラムダ式について
ラムダ式
抽象メソッドが1つ定義されているインターフェースを「関数型インターフェース」といいます。
この関数型インターフェースを実装してインスタンスを生成するのが「ラムダ式」です。
Java8で追加されたメソッドにIterable#forEach()があります。
このIterable#forEach()が引数にとるConsumer super T> actionは
これは、accept(Object)を関数メソッドに持つ関数型インタフェースです。
とあるように関数型インターフェース。
ということでIterable#forEach()を利用して匿名クラスの書き方とラムダ式を比較してみたいと思います。
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
String[] array = { "1", "2", "3" };
List<String> list = Arrays.asList(array);
Consumer<String> action = new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
};
list.forEach(action);
}
}
匿名クラスを書くのにあれこれと追加をしなければならないです。
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class Test {
public static void main(String[] args) {
String[] array = { "1", "2", "3" };
List<String> list = Arrays.asList(array);
Consumer<String> action = (String t) -> {
System.out.println(t);
};
list.forEach(action);
}
}
簡単に書け・・・
あれ?どんな構造になってんだこれ?
(関数型インターフェースのメソッドの引数の型 関数型インターフェースのメソッドの引数) -> {処理;}
この構造を意識して匿名クラスの部分とラムダ式の部分を見比べてみる。
Consumer<String> action = new Consumer<String>() {
@Override
public void accept(String t) {
System.out.println(t);
}
};
Consumer<String> action = (String t) -> {
System.out.println(t);
};
引数と中の処理は残し
newする部分とメソッド名の宣言は省略され
->(アロー記号)を間に挟んでいる。
省略したラムダ式の書き方
実はラムダ式は以下のルールに従い更に省略ができる。
- コンパイラの推論が働く場合は引数の型は不要
list.forEach((t) -> {System.out.println(t);});
Iterable#forEach()の動作は以下のようになる
for (T t : this)
action.accept(t);
List<String>の場合は中はStringという推論がはたらくので
上の「t」の型「String」が省略できるようになる。
- 型の省略をしたのち引数を囲むカッコは引数が1つなら省略できる
list.forEach(t -> {System.out.println(t);});
-
処理が1つの式で完結する場合
- 値を返さないメソッドなら、処理が1つの式で完結する場合、ブロック記号({})や式のセミコロンは省略でき
ラムダ式(処理1つ)list.forEach(t -> System.out.println(t));- 値を返すメソッドなら、処理がreturnの1つの式で完結する場合、更にreturnも省略できる
理解が間違っている部分などありましたらコメントいただけますと嬉しいです。