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... :-) :-)
s'marks
[1]
http://angelikalanger.com/GenericsFAQ/FAQSections/TypeArguments.html#What%20do%20multilevel%20wildcards%20mean?
[2] https://sourceforge.net/p/streamsupport/tickets/125/#2d90
More information about the core-libs-dev
mailing list