2

and() and or() methods of Predicate interface in Java 8 takes any super type of T, that is ? super T and not ? extends T. I was expecting it to take any type that extends T. The motivation for me to think this way is, since T is the type I'm defining my predicate on, the compound predicate also should be on T (that is any subtype of T). What is the reason behind that being ? super T. Can someone help me understand?

Holger
  • 285,553
  • 42
  • 434
  • 765
chandra_cst
  • 307
  • 2
  • 13
  • 1
    See [“What is PECS (Producer Extends Consumer Super)?”](http://stackoverflow.com/q/2723397/2711488) – Holger Mar 14 '17 at 11:19

1 Answers1

2

So here is my understanding of it. Suppose we have this:

interface MyPredicate<T> {
    boolean test(T t);
}

And these declarations:

 MyPredicate<? extends Number> p1 = (Number n) -> n.intValue() > 9;
 MyPredicate<Integer> p3 = null;
 MyPredicate<Long> p4 = null;

Because Integer and Long are sub-types of Number, we can do this:

p1 = p3;
p1 = p4;

At the same time, let's suppose p3 and p4 are not null. The only types they can accept in test would be Integer and Long.

p3.test(12);
p4.test(12L);

But what would p1 accept? Integer, but what if it points to MyPredicate<Long>? Long, but what if it points to MyPredicate<Integer>?

So there is no way to apply p1 to anything but null in this case.

Let's introduce p2:

MyPredicate<? super Number> p2 = (Number n) -> n.intValue() > 10;

Since it uses super we can't even do this:

p2 = p3; // will not compile
p2 = p4; // will not compile

But this time we know that we will have some type that will have Number as it's super type, so we can safely apply Number here. Meaning :

p2.test(12); // 12 is Number

will compile just fine; since we are 100% sure that whatever the type, it is a super type of Number.

Let's change MyPredicate:

    interface MyPredicate<T> {

    boolean test(T t);

    default MyPredicate<T> and(MyPredicate<? extends T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t); // this will not compile
    }
}

Because we have used extends, we don't know the actual type, so it will fail, unlike super:

    interface MyPredicate<T> {

    boolean test(T t);

    default MyPredicate<T> and(MyPredicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t); // compiles just file
    }
}
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Eugene, now I understand why using extends will not be helpful. One small correction to your explanation. `? super Number` can take any type that is a super type of Number. In the above explanation you mentioned it will be guaranteed to extend Number which is not true. – chandra_cst Mar 17 '17 at 22:52