一、初識
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表達式的一種簡化寫法,還原就是如下語句:
即:當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
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIyVGduV2YfNWawNyZuBnLwIzNxITMwATMzATNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)