ラムダとPredicate
このレッスンを終えると、ラムダ式の書き方として正しいものを見分け、Predicate と removeIf の組み合わせを追えるようになります。
まず予測してください
次のうち、コンパイルが通るのはどれでしょうか。
Predicate<Integer> a = n -> n > 10;
Predicate<Integer> b = (n) -> { return n > 10; };
Predicate<Integer> c = (Integer n) -> n > 10;
Predicate<Integer> d = n -> { n > 10; };
Predicate<Integer> e = (int n) -> n > 10;通るのは a と b と c です。d と e が落ちます。
ラムダ式の形の規則
引数が1つのときだけ、かっこを省けます。0個や2個以上のときは省けません。型を書くなら全部の引数に書くか、全部書かないかのどちらかです。片方だけ型を書くと落ちます。
本体を波かっこで囲んだときは、値を返す関数型インターフェースなら return が要ります。d は n > 10; と書いただけで返していないので落ちます。逆に波かっこを使わない式の形では return を書けません。
e が落ちるのは、Predicate<Integer> の引数の型が Integer であって int ではないからです。型を明示するなら宣言どおりの型にします。
関数型インターフェースは抽象メソッドが1つ
ラムダ式を代入できるのは、抽象メソッドがちょうど1つのインターフェースだけです。Predicate<T> が持つ抽象メソッドは boolean test(T t) の1つだけなので、ラムダ式をそのまま代入できます。default メソッドや static メソッドはいくつ持っていても構いません。抽象メソッドを2つ持つインターフェースにラムダを代入させて落とす問題が出ます。
and と or と negate と removeIf
Predicate は and と or と negate を持っています。これらは新しい Predicate を返すだけで、元の Predicate は変わりません。p.negate() を呼んでも p の判定はそのままです。
removeIf は、条件が true になった要素を List から取り除きます。「残す」ではなく「消す」であることを取り違えさせる出題が定番です。
もう1つの注意点として、ラムダの中から使えるローカル変数は実質的に final なものだけです。あとから代入し直している変数をラムダの中で読むと、コンパイルエラーになります。
要件
- 10, 20, 30, 40, 50, 60 をこの順に List へ入れる
- limit より大きいかを判定する Predicate と、20 で割り切れるかを判定する Predicate を作る
- その2つを and でつないだ条件で removeIf する
- 残った List の toString と、1つ目の Predicate を negate したもので 60 を判定した結果を、スラッシュでつないで返す
入出力例
filter(30) → "[10, 20, 30, 50]/false"
filter(100) → "[10, 20, 30, 40, 50, 60]/true"
filter(25) → "[10, 20, 30, 50]/false"