笨读JDK8源码(2)-Predicate

2019/09/17

源码翻译

/**
 * 表示一个参数的谓词(布尔值函数)。
 *
 * <p>这是一个函数式方法为{@link #test(Object)}的函数式接口
 *
 * @param <T> 谓词的输入参数类型
 * @author jdk
 * @since 1.8
 */
@FunctionalInterface
public interface Predicate<T> {

    /**
     * 用给定参数评估谓词的真假。
     *
     * @param t 输入参数
     * @return 若参数满足谓词则返回{@code true},否则返回{@code false}
     */
    boolean test(T t);

    /**
     * 返回一个本谓词和另一个谓词短语与运算的组合谓词。若本谓词为假则另一
     * 个谓词将不被评估。
     *
     * <p>谓词评估期间的任何异常都会被抛到调用处,若本谓词抛出异常,组合的
     * 另一个谓词将不被评估。
     *
     * @param other 一个将被本谓词短路与运算的谓词
     * @return 一个本谓词和另一个谓词短路与运算的组合谓词
     * @throws NullPointerException 若另一个谓词为null则抛出
     */
    default Predicate<T> and(Predicate<? super T> other) {
        // 判空
        Objects.requireNonNull(other);
        // 先评估本谓词,再和另一个谓词的评估结果相与,若本谓词为假则发生短路
        return (t) -> test(t) && other.test(t);
    }

    /**
     * 返回一个本谓词的非谓词。
     *
     * @return 一个本谓词的逻辑非谓词
     */
    default Predicate<T> negate() {
        // 取非
        return (t) -> !test(t);
    }

    /**
     * 返回一个本谓词与另一个谓词短路或运算的组合谓词。若本谓词为真则另一
     * 个谓词将不被评估。
     *
     * <p>谓词评估期间的任何异常都会被抛到调用处,若本谓词抛出异常,组合的
     * * 另一个谓词将不被评估。
     *
     * @param other 一个将被本谓词短路或运算的谓词
     * @return 一个本谓词和另一个谓词短路或运算的组合谓词
     * @throws NullPointerException 若另一个谓词为null则抛出
     */
    default Predicate<T> or(Predicate<? super T> other) {
        // 判空
        Objects.requireNonNull(other);
        // 先评估本谓词,再和另一个谓词的评估结果相或,若本谓词为真则发生短路
        return (t) -> test(t) || other.test(t);
    }

    /**
     * 返回一个根据{@link Objects#equals(Object, Object)}评估两个参数是否
     * 相等的谓词。
     *
     * @param <T>       谓词的输入参数类型
     * @param targetRef 用于比较的对象引用,可能为{@code null}
     * @return 一个根据{@link Objects#equals(Object, Object)}评估两个参数是否相等的谓词
     */
    static <T> Predicate<T> isEqual(Object targetRef) {
        // 判断输入的对象引用是否为空
        return (null == targetRef)
                // 若输入为空则返回一个判空函数 这里返回一个符合boolean test(T t)签名的方法引用即可创建Predicate
                ? Objects::isNull
                // 若输入不为空则返回一个用{@link Objects#equals(Object, Object)}进行比较的谓词
                : object -> targetRef.equals(object);
    }
}

详解

Predicate 是一个泛型函数式接口,相当于离散数学中的谓词逻辑。该接口的实例可通过命题创建,并提供了基于与或非三种逻辑运算的谓词组合方式。

接口的函数式方法是boolean test(T t),实际上就是根据传入的参数 t 判断在创建实例时所使用的命题是否为真。

接口提供了 andornegate 三个用于逻辑组合的默认方法,其中与运算和非运算都是短路运算,先判断当前谓词的真值再判断另一个谓词的真值。

接口还提供了一个静态方法isEqual(Object targetRef),用于创建一个对象相等性判断谓词,所返回的谓词可用于判断targetRef与其它对象是否相等。在需要用同一个对象与多个其它对象进行相等性判断的时候这个函数可为我们提供便利。

使用示例:

public class Main {

    public static void main(String[] args) {
        // 通过Lambda表达式创建谓词
        Predicate<String> p1 = s -> s.length() >= 3;
        // 评估谓词真假
        System.out.println(p1.test("a"));
        System.out.println(p1.test("ab"));
        System.out.println(p1.test("abc"));
        System.out.println(p1.test("abcd"));
        System.out.println("---------------------------");

        Predicate<String> p2 = s -> {
            // 为便于观察短路现象,在这里输出"p2:"
            System.out.print("p2:");
            return s.startsWith("a");
        };
        System.out.println(p2.test("a"));
        System.out.println(p2.test("ab"));
        System.out.println(p2.test("dsad"));
        System.out.println(p2.test("hello"));
        System.out.println("---------------------------");

        // 运算组合 p1 && !p2
        Predicate<String> p = p1.and(p2.negate());
        System.out.println(p.test("abc"));
        System.out.println(p.test("bbbb"));
        // 由于p1为假,会被短路,不输出"p2:"
        System.out.println(p.test("c"));
        System.out.println("---------------------------");

        // 创建一个用于判断其它字符串是否等于"hello world"的谓词
        Predicate<String> pe = Predicate.isEqual("hello world");
        System.out.println(pe.test("hello world"));
        System.out.println(pe.test("hello world!"));
        System.out.println(pe.test("Hello World"));
        System.out.println(pe.test("HelloWorld"));
    }
}

输出:

false
false
true
true
---------------------------
p2:true
p2:true
p2:false
p2:false
---------------------------
p2:false
p2:true
false
---------------------------
true
false
false
false

总结

只要理解了 Java 的函数式接口和函数式编程,Predicate 接口的源码是非常非常易读的,使用方法也很容易掌握。

Predicate 接口只提供了与、或、非三种运算,而没有提供异或、同或等符合运算,也不像 Function 接口那样提供了先执行前置函数再执行本函数的组合方法。

Predicate 接口的用途十分广泛,是一个应当熟练掌握的接口。


(转载本站文章请注明作者和出处)

Show Disqus Comments

Post Directory