一、初識
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 ̄)づ╭❤~
文章推薦: