2

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
    Related: [What is PECS (Producer Extends Consumer Super)?](https://stackoverflow.com/questions/2723397/what-is-pecs-producer-extends-consumer-super). – Slaw Oct 21 '20 at 17:57
  • @Slaw this question is not regarding collections with boundary. assuming its designed based on PECS, the ? extends V is the return type implying value would be read and hence it should should be ? super V right. Can you please clarify – Balaji Krishnan Oct 21 '20 at 18:05
  • 2
    PECS does not only apply to collections. The concept applies to generics in general. A `Function` _consumes_ an object and _produces_ a result. This is also related: [Difference between super T> and extends T> in Java](https://stackoverflow.com/questions/4343202/). – Slaw Oct 21 '20 at 18:09
  • @Slaw, thanks for the clarification. I was of the impression that PECS concept was applicable only to collections as most of the examples use just some collection or other – Balaji Krishnan Oct 21 '20 at 18:40
  • its related to some general answers in PECS but difficult to understand. The user has given clear query which i think should be answered and not be marked as duplicate. – Nishant Lakhara May 28 '21 at 14:28

1 Answers1

4

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.

rgettman
  • 167,281
  • 27
  • 248
  • 326
  • Why would someone create a function MyFunction andThen = first.andThen(second); when he/she knows that the return type of first.andThen(second) is an Integer. – Nishant Lakhara May 28 '21 at 14:39