让开发
成为一种享受!

【睡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接受两个参数
DoublePredicatedoubleboolean接受double类型参数
IntPredicateintboolean接受int类型参数
LongPredicatelongboolean接受long类型参数

好了,有关Predicate接口的介绍到此为止,(づ ̄3 ̄)づ╭❤~

扫码关注公众号:Java开发乐园

在聊天窗口回复:vip

输入验证码,即可永久解锁本站全部文章

扫码关注【Java开发乐园】

Java开发乐园

扫码关注【东方】

微信:东方

验证码:
赞(1) 打赏
转载请注明出处:Java开发乐园 » 【睡JDK】Java函数式编程接口详解之Predicate

来评论一下嘛~ 抢沙发

评论前必须登录!

 

我愿终生等候,换你刹那凝眸

留点🐾印

打赏即是一种肯定,谢谢您的肯定

支付宝扫一扫打赏

微信扫一扫打赏