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));
}
}
Predicate<T>
s take in a T
and give you a boolean
. Yes, it is a producer of boolean
s, 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 :(