java.util.Function source code wildcard boundary usage understanding

Viewed 105

Trying to understand the source code of java.util.Function

Have attempted to create my own version of this class, on comparing my version with source code, found one difference - unable to understand the reason behind usage of a generic type boundary.

myVersion

default <V> MyFunction<T, V> andThen(MyFunction<? super R, V> after) {
        return t -> after.apply(apply(t));
}

javaSource

default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
    Objects.requireNonNull(after);
    return (T t) -> after.apply(apply(t));
}

this is pretty much the same as java.util.Function andThen method except that instead of ? extends V, V has been used.

why is the type upper bound ? extends V used in andThen method - is there any scenario that will only work with ? extends V and not with V

note: I understand the ? super R part is required because without it, we will only be able to chain another function that has the input argument type exactly as R

Have the same doubt about the usage of ? super V in compose method also

1 Answers

The Function interface, and your MyFunction interface, should both be designed with PECS in mind (link What is PECS (Producer Extends Consumer Super)? already supplied by a comment).

While MyFunction can compile and be used with a simple return type of V, it can be made more flexible. This example may be a little contrived but it illustrates the flexibility that can be added by making it ? extends V instead.

MyFunction<Integer, Integer> first = x -> x + 1;
MyFunction<Integer, Integer> second = x -> x * 2;
MyFunction<Integer, Number> andThen = first.andThen(second);

The last line won't compile unless you make the return type include ? extends V. This allows a function variable to be declared to return a wider type. The V is inferred as Number, and the ? extends V part allows it take functions that return Integer, a subtype.

What happens when one needs such flexibility, e.g. when the return type may not be an Integer, but it is a Number?

MyFunction<Integer, Double> third = x -> x / 3.0;
andThen = first.andThen(third);

This is useful when you need to compose functions that may return a different type within the same object hierarchy, e.g. Integer or Double are both Numbers. This flexibility is not possible if you limit the return type to V. The variable andThen can't be used for both results of andThen calls.

Sure, you could force a variable type to be the same as the return type of andThen, but there's no reason to limit the flexibility of a user calling your code.

Producer extends, for flexibility.

Related