Search code examples
javalambdajava-8functional-programmingpecs

How was it decided that Predicate "and" method would have a consumer and not producer in java?


I was going through the Predicate class introduced in java 8 which is functional interface. There is a method and inside the Predicate class which is as following for composing multiple predicates into one.

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

I have read the concept of PECS in java but still couldn't understand why in the case of Predicate we are using ? super T. How have the java programmers decided that it would be a consumer and not a producer.

I mean why lines with compile errors should not be allowed:

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));
    }
}

Solution

  • Predicate<T>s take in a T and give you a boolean. Yes, it is a producer of booleans, but that's not really relevant here. When applying PECS on a type parameter, you should think about whether the type is a producer or consumer of that type parameter.

    Since Predicate<T> accepts a T, it's a consumer of T, so it should be ? super for the T parameter.

    Another example: BiFunction<T, U, V> accepts a T and a U, and produces a V, so it's a consumer of T, consumer of U, and producer of V. Therefore, ? super for T and U, ? extends for V. As you can see, only the type parameters matter. Anything else that the type might do, doesn't.

    The lines will be allowed if you flip the two sides - predInt.and(pred), which creates a Predicate<Integer>.

    This is because and is declared to produce the same type of predicate as the one it is called on, so pred.and(...) can only produce a Predicate<Number>, but a Predicate<Number> doesn't make sense here - the other conjunct can only accept integers!

    You can totally make it work in both orders. If you declare and as a static method:

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

    Then you can do both of these:

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

    But it's less readable this way :(