【睡JDK】Java函數式編程接口詳解之Predicate

一、初識

Predicate是Java提供的重要的函數編程接口之一,作用主要是用於邏輯判斷

首先看看源碼:

@FunctionalInterface
public interface Predicate<T> {

    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }
    
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

對函數式編程接口有一定了解的同學可能會疑惑,爲啥 有這麼多方法,不是說函數式編程接口只有一個方法嗎?確實沒錯,但這個方法只限於沒有實現的方法,不包括有實現的方法,自從Java引入了default關鍵字後,在接口內是可以編寫default方法的。

喏,上面不是還有一個static方法?其實想想也是能想得通的,static方法屬於類的信息,不屬於實例信息,我們平時編碼的時候可能不會這麼寫,但是不代表不可以這麼寫哦。

二、基礎用法

我們現在不必糾結其他方法,把注意力集中在boolean test(T t)方法上。我們先看一段示例代碼:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 6);
List<Integer> list1 = list.stream()
    .filter(num -> num < 5)
    .collect(Collectors.toList());

這段代碼就是過濾列表中小樣5的數字,並生成一個新的列表,當我們點進filter方法的實現,代碼如下:

@Override
    public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
        Objects.requireNonNull(predicate);
        return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                     StreamOpFlag.NOT_SIZED) {
            @Override
            Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
                return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
                    @Override
                    public void begin(long size) {
                        downstream.begin(-1);
                    }

                    @Override
                    public void accept(P_OUT u) {
                        if (predicate.test(u))
                            downstream.accept(u);
                    }
                };
            }
        };
    }

這裏我們暫時不要關心其他代碼,注意點放在兩個地方,一個就是filter的參數列表filter(Predicate<? super P_OUT> predicate),另一個就是predicate.test(u)

看到這個兩個地方,你或許就明白了我們平時常用的filter方法其實就是依賴Predicate函數接口來完成邏輯判斷的。

那我們該如何使用Predicate接口呢?請看用例:

// 邏輯判斷工具
public class LogicUtil {
    public static boolean qualified(Predicate<Person> predicate, Person person) {
        return predicate.test(person);
    }
}

// 測試代碼
public static void main(String[] args) {
    List<Person> list = Arrays.asList(new Person("小明",180), new Person("小剛", 178));
    for (Person person : list) {
        if (LogicUtil.qualified(p -> p.getHeight() >= 180, person)) {
            System.out.println(person.getName() + "身高合格!");
        }
    }
}

這裏只是舉了一很簡單的例子,過濾身高大於180的。這裏我們將過濾的條件由調用者傳入,能夠增加編程的靈活性,也就是說邏輯的判斷由調用者自行實現。

Predicate使用場景推薦:

在一個公共函數內,大部分邏輯是通用的,但是一小部分判斷邏輯是不一樣的,可以使用Predicate作爲公共函數的入參,將那一小部分判斷邏輯通過Lambda表達式的方式傳入公共函數。就像上面filter函數的實現一樣。

三、高階用法

既然是接口,那麼我們當然可以去實現它啦。如果你的判斷邏輯比較複雜,用Lambda表達式比較繁瑣或不夠整潔時,你可以去實現Predicate接口,如下:

// 實現接口
public class Filter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return person.getHeight() >= 180 && Objects.equals(person.getGender(), "man");
    }
}

// 工具
public class LogicUtil {
    public static boolean qualified3(Predicate<Person> filter, Person person) {
        return filter.test(person);
    }
}

// 測試代碼
public static void main(String[] args) {
    List<Person> list = Arrays.asList(new Person("小明",180, "man"),
                                      new Person("小剛", 178, "man"),
                                      new Person("小紅", 190, "woman"));
    for (Person person : list) {
        if (LogicUtil.qualified3(new Filter(), person)) {
            System.out.println(person.getName() + "合格!");
        }
    }
}

這種用法在抽象設計方面有很大的優勢。

四、Predicate的其他方法

文章開頭我們就看到Predicate接口內還有and、negate、or、isEqual等方法,下面就簡單的介紹一下。

4.1 and

先看看and函數的源碼:

default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}

說明:在返回語句中,(t)是lambda表達式的參數,test(t) && other.test(t)是主體。

and函數的功能就是拼接兩個Predicate,返回新的Predicate,看看用法:

// 身高過濾器
public class HeightFilter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return person.getHeight() >= 180;
    }
}

// 性別過濾器
public class GenderFilter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return Objects.equals(person.getGender(), "man");
    }
}

// 測試代碼
public static void main(String[] args) {
    List<Person> list = Arrays.asList(new Person("小明", 180, "man"),
                                      new Person("小剛", 178, "man"),
                                      new Person("小紅", 190, "woman"));
    List<Person> list1 = list.stream()
        .filter(new HeightFilter().and(new GenderFilter()))
        .collect(Collectors.toList());
}

4.2 negate

先看看negate函數源碼:

default Predicate<T> negate() {
    return (t) -> !test(t);
}

函數很簡單,就是返回predicate的否定。

用法:

// 身高過濾器
public class HeightFilter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return person.getHeight() >= 180;
    }
}

// 測試代碼
public static void main(String[] args) {
    List<Person> list = Arrays.asList(new Person("小明", 180, "man"),
                                      new Person("小剛", 178, "man"),
                                      new Person("小紅", 190, "woman"));
    List<Person> list1 = list.stream()
        .filter(new HeightFilter().negate())
        .collect(Collectors.toList());
}

就是返回所有身高小於180的數據人。

4.3 or

default Predicate<T> or(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) || other.test(t);
}

學完and函數,理解or就沒啥問題了,簡單說,就是滿足衆多條件中一個一個即可:

// 身高過濾器
public class HeightFilter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return person.getHeight() >= 180;
    }
}

// 性別過濾器
public class GenderFilter implements Predicate<Person> {
    @Override
    public boolean test(Person person) {
        return Objects.equals(person.getGender(), "woman");
    }
}

// 測試代碼
public static void main(String[] args) {
    List<Person> list = Arrays.asList(new Person("小明", 180, "man"),
                                      new Person("小剛", 178, "man"),
                                      new Person("小紅", 160, "woman"));
    List<Person> list1 = list.stream()
        .filter(new HeightFilter().or(new GenderFilter()))
        .collect(Collectors.toList());
}

此時,小明、小紅都滿足條件

4.4 isEqual

再來看看最後一個靜態方法:

static <T> Predicate<T> isEqual(Object targetRef) {
    return (null == targetRef)
        ? Objects::isNull
            : object -> targetRef.equals(object);
}

咋一看,有的小夥伴可能不是很理解,其實拆分一下也不難。

首先很容易看出裏面是一個三目運算符;

其次Objects::isNull就是lambda表達式的一種簡化寫法,還原就是如下語句:

object -> Objects.isNull(object)

即:當targetRef=null時,Objects.isNull(object)的結果就是Predicate的結果

否則,返回object -> targetRef.equals(object)

用法:

// 測試代碼
public static void main(String[] args) {
    Person xiaoming = new Person("小明", 180, "man");
    List<Person> list = Arrays.asList(xiaoming,
                                      new Person("小剛", 178, "man"),
                                      new Person("小紅", 160, "woman"));
    List<Person> list1 = list.stream()
        .filter(Predicate.isEqual(xiaoming))
        .collect(Collectors.toList());
}

五、Predicate的其他變體

接口名 參數 返回類型 描述
BiPredicate (T, U) boolean 接受兩個參數
DoublePredicate double boolean 接受double類型參數
IntPredicate int boolean 接受int類型參數
LongPredicate long boolean 接受long類型參數

好了,有關Predicate接口的介紹到此爲止,(づ ̄3 ̄)づ╭❤~
文章推薦:

【睡JDK】Java函數式編程接口詳解之Consumer、Function

【睡JDK】Java函數式編程接口詳解之Supplier

【睡JDK】Java函數式編程接口詳解之UnaryOperator、BinaryOperator
end
Java開發樂園

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章