2

我正在浏览 Java 8 中引入的 Predicate 类,它是功能接口。Predicate 类内部有一个方法 and ,用于将多个谓词组合为一个。

default Predicate<T> and(Predicate<? super T> other) {
    Objects.requireNonNull(other);
    return (t) -> test(t) && other.test(t);
}

我已经阅读了 Java 中 PECS 的概念,但仍然不明白为什么在 Predicate 的情况下我们使用? super T. java程序员如何决定它将是消费者而不是生产者。

我的意思是为什么不应该允许出现编译错误的行:

public class PredicateExample {
    public static void main(String[] args) {
        Predicate<Number> pred = n -> n.intValue() > 2;
        Predicate<Integer> predInt = n -> n > 3;
        //Compile error
        //pred.and(predInt);

        Predicate<Object> predObj = o -> Integer.parseInt(o.toString()) > 4;
        pred.and(predObj); //Valid statement

        Number n = new Integer(100);
        System.out.println(pred.and(predObj).test(10));
        System.out.println(predInt.and(pred).test(10));
        //Compile error
        //System.out.println(pred.and(predInt).test(10));
    }
}
4

3 回答 3

6

Predicate<T>s 接受 aT并给你 a boolean。是的,它是booleans 的生产者,但这在这里并不重要。在类型参数上应用 PECS 时,您应该考虑该类型是该类型参数的生产者还是消费者。

由于Predicate<T>接受 a T,它是 的消费者T,所以它应该是? super参数T

另一个例子:BiFunction<T, U, V>接受 aT和 a U,并产生 a V,所以它是 的消费者T、消费者U和生产者V。因此,? super对于TU? extends对于V。如您所见,只有类型参数很重要。该类型可能做的任何其他事情都不会。

如果您翻转两侧 - 将允许这些线条predInt.and(pred),这会创建一个Predicate<Integer>.

这是因为and被声明为产生与调用它的谓词相同类型的谓词,所以pred.and(...)只能产生 a Predicate<Number>,但 aPredicate<Number>在这里没有意义 - 另一个连词只能接受整数!

你完全可以让它在两个订单中都起作用。如果您声明and为静态方法:

public static <T> Predicate<T> and(Predicate<? super T> a, Predicate<? super T> b)

然后你可以做这两个:

and(predInt, pred)
and(pred, predInt)

但这种方式可读性较差:(

于 2021-05-29T08:19:02.533 回答
4

Producer - 通过返回一个对象给你: 这个对象必须是它的一个或一个子类,所以它。T get()
Textends

消费者- 从您那里获取一个对象作为方法参数: 该方法可以采用更通用的类型,但不能要求更具体的类型,因此参数可以是.void accept(T)
super

由于 Predicateboolean test(T)方法将对象作为参数,因此它是消费者。

参考:什么是PECS(Producer Extends Consumer Super)?

于 2021-05-29T08:08:41.413 回答
3

问题与

    Predicate<Number> pred = n -> n.intValue() > 2;
    Predicate<Integer> predInt = n -> n > 3;
    //Compile error
    //pred.and(predInt);

是接受(例如也)的pred所有子类,但只接受一个特定的子类()。NumberDoublepredIntInteger

因此,以下代码很好:

    Number n = Double.valueOf(100);
    System.out.println(pred.test(n));

但是以下代码(如果可以编译)会在运行时失败:

    Number n = Double.valueOf(100);
    System.out.println(pred.and(predInt).test(n));

由于引入泛型正是为了防止此类运行时故障,它必须防止代码编译(即产生编译错误)。

于 2021-05-29T08:39:34.740 回答