huoenter
huoenter

Reputation: 522

The signature of flatMap in Optional of Java 8

In the oracle docs, it appears to be

<U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper)

For mapper as a Function, it makes the parameter contra-variant but does not make the return type covariant. I wonder if the mapper can (should) be

Function<? super T,Optional<? extends U>>

or

Function<? super T, ? extends Optional<? extends U>>

?

Upvotes: 13

Views: 492

Answers (2)

huoenter
huoenter

Reputation: 522

First of all, IMO, since U binds to the method itself but not the class Optional and also Optional is final, the current signature should work fine.

If either the two conditions above does not hold, then changes can be applied. Thanks for the link provided by @MalteHartwig. Let me summarize the answer to this particular question. It becomes clear that if the return type needs to be covariant, the latter signature (the longer one) is necessary in Java 8. It is not only about inheritance. ? extends is needed in front of Optional<? extends U> to declare the user-site variance of Function even when Optional is final. I made a code snippet to demonstrate it:

import java.util.function.Function;

class A {}
class B extends A {}

final public class Option<T> {
  private T value;
  public Option(T v) { value = v; }
  <U> Option<? extends U> flatMap1(Function<? super T, Option<? extends U>> mapper) {
    return mapper.apply(value);
  }

  <U> Option<? extends U> flatMap2(Function<? super T, ? extends Option<? extends U>> mapper) {
    return mapper.apply(value);
  }

  void test() {
    Option<A> oa = new Option<>(new A());
    Function<A,Option<A>> faa = (A a) -> new Option<>(new A());
    Function<A,Option<B>> fab = (A a) -> new Option<>(new B());
    //oa.flatMap1(faa);   DOES NOT COMPILE
    oa.flatMap2(fab);
  }
}

It seems that since Java only has user-site variance declaration, you may need a series of ? extends that propagate that declaration all the way to the (2nd) outermost level from the type variable you want to declare variance for.

Upvotes: 1

Zachary
Zachary

Reputation: 1703

Say you have class A and class B extends A; you need to remember that an Object B is an A Object; You inherit from it.

In order for an Object to fulfill the Optional<? extends U> constraint, it must have fulfilled the Optional<U> constraint. Likewise, in order for an Object to fulfill the Optional<U> constraint, it must have fulfilled Optional<? extends U>.

They are, in this situation, synonymous terms.

While the question is specific to a certain method, you can test for the answer fairly easily. By writing a similarly function, you can test whether the compiler would allow what you want and whether it would run as expected.

public static <U> void List(List<U> A, List<? extends U> B) {
    ...
}
...
MakeList(new ArrayList<Number>(), new ArrayList<Integer>());

As expected, everything is fine.

Upvotes: 0

Related Questions