它是如何决定谓词“和”方法在 java 中将有一个消费者而不是生产者的?

Posted

技术标签:

【中文标题】它是如何决定谓词“和”方法在 java 中将有一个消费者而不是生产者的?【英文标题】:How was it decided that Predicate "and" method would have a consumer and not producer in java? 【发布时间】:2021-08-17 07:17:09 【问题描述】:

我正在研究 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));
    

【问题讨论】:

目标是创建一个Predicate,它可以为T 类型的对象做出决定。处理T 的超类的谓词也可以决定T 的事情。例如,假设您为狗创建了一个谓词Predicate&lt;Dog&gt;,然后您想将它与处理Animals、Predicate&lt;Animal&gt; 的谓词组合,例如一个可以决定动物是否超过10 年的谓词老的。这样的谓词也可以用于狗。因此,当一般的动物谓词也适用时,不必将自己仅限于狗的谓词。 @Zabuzard 评论等同于我的回答。谢谢 【参考方案1】:

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

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

由于 Predicate boolean test(T) 方法将对象作为参数,所以它是一个消费者。

参考:What is PECS (Producer Extends Consumer Super)?

【讨论】:

【参考方案2】:

Predicate&lt;T&gt;s 接收T 并给你一个boolean。是的,它是booleans 的生产者,但这在这里并不重要。 在对类型参数应用 PECS 时,您应该考虑该类型是该类型参数的生产者还是消费者

由于Predicate&lt;T&gt; 接受T,它是T 的消费者,因此T 参数应该是? super

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

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

这是因为and 被声明为产生与调用它的谓词相同类型的谓词,所以pred.and(...) 只能产生Predicate&lt;Number&gt;,但Predicate&lt;Number&gt; 在这里没有意义 -其他连词只能接受整数!

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

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

那么你可以做到这两点:

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

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

【讨论】:

【参考方案3】:

问题

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

pred 接受Number 的所有子类(例如Double),但predInt 只接受一个特定的子类(Integer)。

因此下面的代码很好:

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

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

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

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

【讨论】:

如果我做对了,OP 知道这一切,并专门询问 PECS 的术语(请参阅他的问题和评论部分中的详细信息)。

以上是关于它是如何决定谓词“和”方法在 java 中将有一个消费者而不是生产者的?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Java 中动态构造谓词

如何在 Android Java 中将 MAC 地址转换为 IP?

如何从 Java 中的自定义谓词列表中创建一个谓词?

如何否定方法引用谓词

iOS Swift 复合谓词

如何将多个谓词应用于 java.util.Stream?