Thiyagu
Thiyagu

Reputation: 17910

Why are Optional's or and flatMap methods' supplier type parameters wildcards?

The Optional.or method was added in Java 9. This is the method signature

public Optional<T> or​(Supplier<? extends Optional<? extends T>> supplier)

Why is the type parameter of the Supplier taking ? extends Optional rather than just Optional, since Optional is a final class?

The same is true for the Optional.flatMap method. This is a change from Java 8.

In Java 8, it was Function<? super T, Optional<U>> mapper whereas it was changed to Function<? super T,​? extends Optional<? extends U>> in Java 9.

Upvotes: 30

Views: 1591

Answers (3)

Eugene
Eugene

Reputation: 120988

Yeah... it is said that wildcard with an extends-bound (upper bound) makes the type covariant, which means that for example List<Apple> is an actual subtype of List<? extends Fruit> (considering that Apple extends Fruit); this is also called covariance.

Or in the examples that you have shown, it means that Optional<StringBuilder> is a subtype of Optional<? extends Optional<? extends CharSequence>>, so you could for example do:

List<Optional<String>> left = new ArrayList<>();
List<? extends Optional<? extends CharSequence>> right = new ArrayList<>();

right = left; // will compile

or assign a Function to the other

Upvotes: 9

Stefan Zobel
Stefan Zobel

Reputation: 3212

FWIW, a similar issue with covariant arguments still exists in Stream.iterate and Stream.iterate in Java 11. The current method signatures are

static <T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next)
static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)

These signatures do not allow for some combinations of seeds and UnaryOperators that are sound from a type perspective, e.g. the following doesn't compile:

UnaryOperator<String> op = s -> s; 
Stream<CharSequence> scs = iterate("", op); // error

The proposed solution is to change the method signatures to

static <T, S extends T> Stream<T> iterate(S seed, Predicate<? super S> hasNext, UnaryOperator<S> next)
static <T, S extends T> Stream<T> iterate(S seed, UnaryOperator<S> f)

So, in contrast to Optional.or and Optional.flatMap this is a case where the "additional-type-parameter approach" actually works.

Upvotes: 11

Thiyagu
Thiyagu

Reputation: 17910

I found the reasoning behind this from Stuart Marks himself

http://mail.openjdk.java.net/pipermail/core-libs-dev/2016-October/044026.html

This has to do with nested generics (Optional is nested within Function). From the mail thread

 Function<..., Optional<StringBuilder>>

is not a subtype of

 Function<..., Optional<? extends CharSequence>>

To get around this, we have to add the outer wildcard as well, so that

 Function<..., Optional<StringBuilder>>

is a subtype of

 Function<..., ? extends Optional<? extends CharSequence>>

Upvotes: 37

Related Questions