Reputation: 307
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?
Upvotes: 2
Views: 210
Reputation: 120848
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
}
}
Upvotes: 2