メソッド参照でNOT,ORを実現 | Java8

はじめに

これはJava8のStreamの美しさに完全に毒された私による個人メモです。

メソッド参照の型キャスト

メソッド参照の表現を型キャストを使ってそのまま関数オブジェクトにすることができます。

極端な例にはなりますが、例えば

Predicate<String> pred = String::isEmpty;
boolean b = pred.test("");

boolean b = ((Predicate<String>) String::isEmpty).test("");

と同値です。

例題

以下のリストから、大人ではなく、
且つ、(女性または長い名前を持つ)人を全て挙げよ。
class Person {
   public final String name;
   public final String gender;
   public final int age;

   public Person(String name, String gender, int age) {
       this.name = name;
       this.gender = gender;
       this.age = age;
   }

   public boolean isMale() {
       return gender.equals("Male");
   }

   public boolean isFemale() {
       return gender.equals("Female");
   }

   public boolean isAdult() {
       return age >= 20;
   }

   public boolean hasLongName() {
       return name.length() >= 5;
   }
}            
public static void main(String[] args) {
    List<Person> people = List.of(
           new Person("高羽義哉", "Male", 10),
           new Person("五十川日出雄", "Male", 21),
           new Person("加賀屋鷹一", "Male", 16),
           new Person("上尾博重", "Male", 19),
           new Person("栗屋竜輝", "Male", 21),
           new Person("毎熊宜典", "Male", 19),
           new Person("八須常宏", "Male", 23),
           new Person("滝内ことり", "Female", 10),
           new Person("斯波柊", "Female", 23),
           new Person("佐村由郁", "Female", 24),
           new Person("木名瀬佳洋子", "Female", 18),
           new Person("織戸誉子", "Female", 10)
    );
}

拡張for文を使ったやり方

for (Person person : people) {
   if (!person.isAdult()) {
       if (person.isFemale() || person.hasLongName()) {
           System.out.println(person);
       }
   }
}

Stream.filterを使ったやり方

people.stream()
   .filter(((Predicate<Person>) Person::isAdult).negate())
   .filter(((Predicate<Person>) Person::isFemale).or(Person::hasLongName))
   .forEach(System.out::println);

2行目ではnegate()を用いてNOTを表し、3行目ではor(Predicate<T>)を用いてORを表しています。

自分でORを実装

カッコ表示が多くて少し見ずらいので、簡潔にメソッド参照のみでORを実現する方法を考えました。こいつです。

public static <T> Predicate<T> any(Predicate<T>... predicates) {
   return t -> Arrays.stream(predicates).anyMatch(predicate -> predicate.test(t));
}

このメソッドは、複数のPredicate<T>が全てtrueを示す時だけtrueを示すPredicate<T>を作ります(早口言葉)。

なのでさっきの3行目

.filter(((Predicate<Person>) Person::isFemale).or(Person::hasLongName))

.filter(any(Person::isFemale, Person::hasLongName))

と置き換えることができます。

簡潔になった!!

この記事が気に入ったらサポートをしてみませんか?