Search code examples
javagenericsinterfacecasting

static functions using generic types in Java


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?


Solution

  • 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