【睡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开发乐园

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