Predicate インターフェース
if に書いた条件は、その場所から動かせない
「このタグは有効か」という判定を、登録画面と一覧画面と集計処理の 3 か所で書いたとします。中身は同じ条件式なのに、if の中に直接書いてあるので共有できません。仕様が変わると 3 か所を探して直すことになり、1 か所直し忘れて挙動がずれる、というのがよくある事故です。
条件そのものを変数に入れて持ち回せれば、直す場所は 1 か所で済みます。そのための型が java.util.function.Predicate<T> です。
条件を変数に入れて、test で判定する
Predicate<T> は「T を 1 つ受け取って true か false を返す」という意味の型です。動かす命令は test の 1 つだけで、戻り値は必ず boolean です。
Java
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
public class Demo {
public static void main(String[] args) {
Predicate<String> isBlank = s -> s.isBlank();
System.out.println(isBlank.test("java")); // false
System.out.println(isBlank.test(" ")); // true
List<String> tags = new ArrayList<>(List.of("java", " ", "web"));
tags.removeIf(isBlank);
System.out.println(tags); // [java, web]
}
}tags.removeIf(isBlank) のところが大事です。List.removeIf は引数に Predicate を取る作りになっているので、自分で作った条件をそのまま渡せます。Stream の filter も同じです。条件に名前が付くと、読む側は中身を追わなくても何を選んでいるのか分かります。
test が返せるのは boolean だけ
つい「値を返すラムダ」を書いてしまうことがありますが、これはコンパイルエラーになります。
Java
Predicate<Integer> half = n -> n / 2; // エラー、boolean ではないtest の戻り値は boolean に固定されていて、それ以外の型は返せません。値を別の値に変える役目は Function が持っています。
もう 1 つ、Predicate<int> とは書けません。ジェネリクスはプリミティブ型を受け取れないので、整数を判定するならラッパー型の Integer を書きます。
Java
Predicate<Integer> isEven = n -> n % 2 == 0;
int size = 4;
System.out.println(isEven.test(size)); // truesize は int ですが、test に渡すところでオートボクシングが働くので、呼ぶ側で変換を書く必要はありません。
条件が「1 つ受け取って真偽を返す」形になっているかどうかで判断します。真偽以外を返したくなった時点で、それは
Predicateの仕事ではありません。
課題では、判定の条件を Predicate の変数として作り、test を通した結果で数えます。if に条件式を直接書いても数は合いますが、それでは条件を取り出したことにならないので、必ず test を経由させてください。
要件
- メソッドの中で
Predicate<Integer> isPositive = n -> n > 0;をラムダ式で作ること - 配列の各要素に対して
isPositive.test(n)を呼び、trueのものだけ数えること (if (n > 0)の直書きはせず、必ずPredicateを経由する) - ファイル先頭で
import java.util.function.Predicate;を書くこと
入出力例
positiveCount([-1,2,-3,4,5]) → 3
positiveCount([]) → 0
positiveCount([0]) → 0
positiveCount([-1,-2,-3]) → 0
positiveCount([1,2,3,4]) → 4
positiveCount([0,1,0,2,0,3]) → 3