RFR(s): 8152617 add missing wildcards to Optional or() and flatMap()

Stuart Marks stuart.marks at oracle.com
Fri Oct 7 19:22:09 UTC 2016

On 10/7/16 11:23 AM, Paul Sandoz wrote:
>>    flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)
> Optional is final so why do you need to express “? extends Optional” ?

The short answer is, it doesn't work if you don't have it. :-)

The theoretical answer is that in this context, "? extends P<Q>" means "some 
subtype of P<Q>" and not necessarily just a subclass of P.

(And even though Optional is final, it's not "permanently final" in that a 
hypothetical future version of the class library might change it to non-final, 
allowing subclasses.)

This issue is covered in Angelika Langer's Generics FAQ entry, "What do 
multi-level (i.e., nested) wildcards mean?" [1] Note that the answer begins, "It 
depends." :-) I also note in passing that I've read this about five times and 
I'm still not quite sure I understand it entirely.

For me, the best explanation comes from looking at examples. First, the history 
is that the signature in Java 8 is:

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

I believe Rémi originally proposed something like this (although it was about 
or(), the same issue applies to flatMap()):

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

The suggested fix that ended up in bug report was this:

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

But this doesn't work for reasons I explain below. Finally, I'm proposing:

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

In researching old email threads and chasing links, I ran across an example from 
Stefan Zobel [2] that fails with #3. He had an alternative that didn't seem 
quite right to me, so I ended up with #4.

I've adapted Stefan's example as follows:

	Optional<Integer> oi = Optional.empty();
	Function<Number, Optional<StringBuilder>> fm = n -> Optional.empty();
	Optional<CharSequence> ocs = oi.flatMap(fm);

The flatmapper function 'fm' returns Optional<StringBuilder>. In the assignment 
on the last line, U is CharSequence, so we can compare this to the various 
signatures shown above with U filled in.

Case #1 fails because Optional<StringBuilder> is incompatible with 
Optional<CharSequence>. This is the usual "generics are invariant thing". Even 
though SB is a subtype of CS, Optional<SB> isn't a subtype of Optional<CS>.

Case #2 fails because adding a wildcard there doesn't help matters, since 
Optional<SB> is still unrelated to Optional<CS>.

Now for the tricky part. :-)

Surely case #3 should work, because adding an inner wildcard provides a 
subtyping relationship, so Optional<SB> is a subtype of Optional<? extends CS>.

That much is true, but these are nested within the Function<> generic type, so 
the "generics are invariant" rule applies again. Thus,

     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>>

So that's what ended up in the signature.

Similar analysis applies to the or() case.

Now awaiting a message from Rémi telling me my explanation is incorrect in 3... 
2... 1... :-) :-)



[2] https://sourceforge.net/p/streamsupport/tickets/125/#2d90

More information about the core-libs-dev mailing list