I was looking at the source code of Predicate in java.util.function, (which for context, is a functional interface that returns a boolean value having taken some generic input of type T) when I came across the following functions:
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
@SuppressWarnings("unchecked")
static <T> Predicate<T> not(Predicate<? super T> target) {
Objects.requireNonNull(target);
return (Predicate<T>)target.negate();
}
and I am puzzled by how static functions can be used with all these generic types lying around. What's going on?
I was expecting some type inference makes this happen. In the first method, the return function is a function that can be fed into Predicate<T>
implicitly, but since the targetRef
argument is of type Object
, I would expect that this can only be made into a class of type Predicate<Object>
, which seems to me, defeats the point of using T
at all in the function.
The second function, I cannot even think of a reasonable inference pattern, it seems absurd. The return type is cast into Predicate<T>
, but target.negate
will return Predicate<? super T>
, which makes taking a super argument itself strange, and further, because of the typecast in the return statement, and the obscurity in the argument as well, it is not possible to know a sensible T
from either. How do either of these methods work, and why were they defined that way?
We can always specify the types explicitly:
static <T> Predicate<T> isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: (T object) -> targetRef.equals(object);
}
So while targetRef
is of type Object
, object
is of type T
.
T
depends on the calling context, e.g.:
Predicate.isEqual(something); // T == Object
Predicate.<String>isEqual(something); // T == String
Predicate<Double> p = Predicate.isEqual(something); // T == Double
Stream.of(1,2,3).filter(Predicate.isEqual(2)); // T == Integer