【睡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 ̄)づ╭❤~
文章推荐:

【睡JDK】Java函数式编程接口详解之Consumer、Function

【睡JDK】Java函数式编程接口详解之Supplier

【睡JDK】Java函数式编程接口详解之UnaryOperator、BinaryOperator
end
Java开发乐园

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JDK 1.8中,函数式接口(Functional Interface)是一个核心概念。如果一个接口有且只有一个未实现的方法,那么这个接口就被称为函数式接口。在JDK 1.8中引入了一个新的注解@FunctionalInterface,将这个注解放在接口定义之前,就表示这个接口是一个函数式接口。编译器会检查该接口是否只有一个未实现的方法,如果定义了多个或者没有定义,编译器会报错。尽管这个注解不是必须的,但虚拟机仍然能够识别出函数式接口。这个注解的主要作用是防止误操作,加了这个注解之后接口将不允许出现多个未实现的方法。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [jdk1.8之函数式接口](https://blog.csdn.net/liu_shi_jun/article/details/128562977)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [JDK1.8新特性--函数式接口(详解)](https://blog.csdn.net/Saintmm/article/details/109557103)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值