ラムダ式の基本
中身は 1 行なのに、まわりに 5 行付いてくる
金額を表示用の文字列に整える処理を、後から差し替えられるようにしたいとします。Java でこれをやるには、まず入り口になるインタフェースを 1 つ用意します。
Java
interface Formatter {
String format(int amount);
}そして中身を渡します。Java 7 まではこう書くしかありませんでした。
Java
Formatter yen = new Formatter() {
@Override
public String format(int amount) {
return amount + " 円";
}
};名前を付けないクラスをその場で作って渡す書き方で、匿名クラスと呼びます。書きたいのは amount + " 円" の 1 行だけなのに、new Formatter()、@Override、メソッドの宣言、閉じ括弧と、まわりに 5 行付いてきます。中身より外側の方が長い状態です。
メソッドが 1 つなら、名前は書かなくても決まる
よく見ると、この 5 行はどれも「書かなくても分かる」ものです。Formatter にはメソッドが 1 つしかないので、渡した中身がどのメソッドの実装なのかは、書かなくても決まります。型も、変数の宣言側に Formatter と書いてあります。
Java 8 からは、その決まりきった部分を全部省けるようになりました。
Java
Formatter yen = amount -> amount + " 円";-> の左が引数、右が返す値です。これがラムダ式です。amount の型も書いていませんが、Formatter の format が int を受け取ると分かっているので、コンパイラが埋めてくれます。呼び方は匿名クラスのときと変わりません。
Java
System.out.println(yen.format(1200)); // 1200 円1 行で収まらないときは、{ } で囲んで return を書きます。
Java
Formatter taxIncluded = amount -> {
int total = amount * 11 / 10;
return total + " 円 (税込)";
};書けるのは、メソッドが 1 つだけのインタフェース
ラムダ式は、どんなインタフェースにも使えるわけではありません。メソッドが 1 つしかないインタフェースだけです。2 つあると、書いた中身がどちらの実装なのか決められないからです。
自分で作るインタフェースには @FunctionalInterface を付けておくと安心です。うっかり 2 つ目のメソッドを足したときに、コンパイラが「これはもうラムダ式で書けない」と教えてくれます。
Stream のメソッドが軒並みラムダ式を受け取れるのは、引数の型がすべてこの「メソッドが 1 つだけ」のインタフェースだからです。
filterに渡していたn -> n > 0も、sortedに渡していた比べ方も、正体はその場で作った実装でした。
やってみよう
Solution.lambdaSum(int[] arr) を static メソッドとして完成させて、配列の合計を返してください。
Arrays.stream(arr) で作った IntStream を boxed() で Stream<Integer> に変え、mapToInt にラムダ式を渡してから sum() で合計を取ります。遠回りに見えますが、Stream<Integer> から数値の Stream に戻すところでラムダ式を 1 つ書く、という流れをここで通しておくためです。
mapToInt に渡す式は、受け取った値をそのまま返すだけで足ります。空配列のときは合計が 0 になるので、長さを調べる if は要りません。
要件
Solution.lambdaSum(int[] arr)のstaticメソッドとして実装することArrays.stream(arr)でint[]をIntStreamに変換し、.boxed()でStream<Integer>に変えてからmapToInt(n -> n)でラムダ式を適用すること- 最終的に
sum()を呼んで合計をintで返すこと (空配列なら自動的に0が返る)
入出力例
lambdaSum([1,2,3]) → 6
lambdaSum([]) → 0
lambdaSum([10,20,30,40]) → 100
lambdaSum([-5,5,-10,10]) → 0
lambdaSum([7]) → 7