logo头像

From zero to HERO

Java 8 中的函数式断言Predicate

最近在搞Excel导入,数据校验是少不了的,但是不同的数据字段有不同的校验策略,五花八门的,甚至不确定,没有办法使用JSR303。所以就搞一个校验策略工具,把校验策略抽象出来。这里尝试了Java 8 提供的一个断言函数接口java.util.function.Predicate<T>,非常给力!把校验策略完美地封装了起来。

Predicate<T> 接口

Predicate的应用

先来看看效果:

    boolean validated = new Validator<String>()
            .with(s -> s.length() > 5)
            .with(s -> s.startsWith("felord"))
            .with(s -> s.endsWith("cn"))
            .with(s -> s.contains("."))
            .validate("felord.cn");

我拿校验字符串为例子,通过一系列的Predicate<String>断言来对字符串felord.cn进行校验。这里不局限于String提供的方法,只要你满足 String -> boolean,也就是一个字符串入参能得到一个布尔返回值,都可以作为条件。例如我们在数据库中检索某个字符串,我们可以写一个非常常见的UserService查询用户名是否存在或者有效:

public class UserServiceImpl implements UserService {
    @Override
    public boolean checkUserByName(String name) {
        return false;
    }
}

对应的校验可以改为:

    UserServiceImpl userService = new UserServiceImpl();
    boolean validated = new Validator<String>()
            .with(s -> s.length() > 5)
            .with(s -> s.startsWith("felord"))
            .with(userService::checkUserByName)
            .validate("felord.cn");

好奇的同学该想知道是怎么实现的,Validator<T>源码是这样的:

import java.util.function.Predicate;

public class Validator<T> {
    /**
     * 初始化为 true  true &&其它布尔值时由其它布尔值决定真假
     */
    private Predicate<T> predicate = t -> true;

    /**
     * 添加一个校验策略,可以无限续杯😀
     *
     * @param predicate the predicate
     * @return the validator
     */
    public Validator<T> with(Predicate<T> predicate) {
        this.predicate = this.predicate.and(predicate);
        return this;
    }

    /**
     * 执行校验
     *
     * @param t the t
     * @return the boolean
     */
    public boolean validate(T t) {
        return predicate.test(t);
    }
}

逻辑不是很复杂,却可以胜任各种复杂的断言策略组合。接下来我们来对Predicate<T> 一探究竟。

Predicate

@FunctionalInterface
public interface Predicate<T> {

    /**
     *  函数接口方法
     */
    boolean test(T t);

    /**
     * and 默认方法 相当于逻辑运算符 &&
     */
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    /**
     * negate 默认方法 相当于逻辑运算符 ! 
     */
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    /**
     * or 默认方法  相当于逻辑运算符 ||
     */
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    /**
     *  这个方法是提供给{@link Objects#equals(Object, Object)}用的,不开放给开发者
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

断言函数接口提供了test方法供我们开发实现,同时提供了andnegateor分别对应Java中的逻辑运算符&&!||。完全满足了布尔型变量运算,在需要多个条件策略组合时非常有用。

总结

今天通过演示了Predicate<T>的使用,回答了曾经一个同学到底lambda表达式和函数式编程到底如何使用的问题。函数式编程在Java的诞生,“消灭”了很多设计模式,尤其是策略模式。如果你想用好函数式编程就要加强抽象能力,一定不要强行的函数式。玩出类似下面这行代码的笑话:

    if (Objects.deepEquals(bool,true)){
            //TODO
    }

好了,今天的分享就到这里,多多关注,点赞,转发,就是对我的最大支持。

评论系统未开启,无法评论!