Hi,
I’m going to challenge the consensus a little bit. First, Rémi's example can be
simplified to this one method which fails to compile with the same error
message[0]:
private static Optional<?> simple1(
Optional<String> o, Function<String, Optional<?>> f
) {return o.flatMap(f);}
Second, although not in the webrev, Optional::map and Optional::flatMap can be
implemented without needing any unchecked casts:
public <U, R extends U, O extends Optional<R>> Optional<U> flatMap(
Function<T, O> mapper
) {
Objects.requireNonNull(mapper);
return ofNullable(isPresent() ? mapper.apply(get()).orElse(null) : null);
}
public <P, Q extends P> Optional<P> map(
Function<T, Q> mapper
) {
Objects.requireNonNull(mapper);
return ofNullable(isPresent() ? mapper.apply(get()) : null);
}
These are fully, soundly compile-time typed methods with all types exposed and
completely under the control of the programmer.
Now, to see if the signature of flatMap is truly the problem, it’s possible to
write the simple1 method as a map followed by a flatMap with the identity
function, like this:
private static Optional<?> simple2(
Optional<String> o, Function<String, Optional<?>> f
) {return o.map(f).flatMap(Function.identity());}
In this version, the call to flatMap and its argument don’t use any wildcard
types, both in the version above and in java.util.Optional[1]. Despite that,
the compiler from jdk1.8.0_102 still gives an error[2] from the flatMap method
call. This is quite a curious result as here flatMap and identity are reusing
types that are already used by type inference. If this isn’t sound but using
wildcards is then I would really like to see that counterexample!
Some questions that have arisen (and my answers):
Should APIs account for types that are not denotable in source code? I’d say
no, such types are bugs in the language.
Can non-denotable types be eliminated at the library level by adding more
wildcards? Unlikely, as such types come from using wildcards to begin with.
Is there a bug in flatMap or is the bug in the language specification? Compared
to one simple method, type inference rules are much harder to understand and
thus more likely to contain undiscovered problems.
What is gained if these wildcards are added? Although simple1 and simple2 would
compile, you still can’t do anything interesting like calling orElse or
orElseGet:
public static void main(String[] args) {
Optional<String> foo = Optional.ofNullable(args[0]);
Object o1 = simple1(foo, s -> foo).orElse(o1 = null);
Object o2 = simple2(foo, s -> foo).orElseGet(() -> null);
}
Won’t compile. As far as I can tell, there is no real upside to this change.
Thanks for your consideration!
[0] Error:(18, 20) java: method flatMap in class java.util.Optional<T> cannot
be applied to given types;
required: java.util.function.Function<? super
java.lang.String,java.util.Optional<U>>
found: java.util.function.Function<java.lang.String,java.util.Optional<?>>
reason: cannot infer type-variable(s) U
(argument mismatch;
java.util.function.Function<java.lang.String,java.util.Optional<?>> cannot be
converted to java.util.function.Function<? super
java.lang.String,java.util.Optional<U>>)
[1] The <? super T> type can be discounted as its presence doesn’t make a
difference in this case.
[2] Error:(21, 27) java: method flatMap in class wildcards.another.Optional<T>
cannot be applied to given types;
required: java.util.function.Function<wildcards.another.Optional<?>,O>
found: java.util.function.Function<java.lang.Object,java.lang.Object>
reason: inference variable O has incompatible bounds
equality constraints: wildcards.another.Optional<?>,T
upper bounds: wildcards.another.Optional<capture#11 of ?>,java.lang.Object
--
Have a nice day,
Timo
Sent from Mail for Windows 10
From: Stuart Marks
Sent: Saturday, October 8, 2016 02:28
To: Stefan Zobel
Cc: core-libs-dev
Subject: Re: RFR(s): 8152617 add missing wildcards to Optional or() andflatMap()
On 10/7/16 3:12 PM, Stefan Zobel wrote:
>> ... After having looked at lots of generic APIs, it seems to me that a
>> style has emerged where wildcards are used whenever possible, and type
>> parameters are used only when necessary, ...
>
> Yes, I'm familiar with that kind of reasoning (I think Josh Bloch stated
> that principle in "Effective Java"). But, to be honest, I never thought
> that it should apply as a strict rule in all circumstances.
Yep, it's in Effective Java, near the end of Item 28:
“As a rule, if a type parameter appears only once in a method
declaration, replace it with a wildcard.”
> Rhetorical question: do you really think that a signature employing 3
> wildcards is easier to understand for the proverbial "average Joe" than
> a bounded type parameter that expresses the sub-type relationship clearly?
> I do not.
>
> But anyway, you probably have to follow the established "style".
>
> It's just too bad that most Java programmers I know won't understand the
> proposed signature of 'flatMap'.
Turns out that Rémi's example exposes the difference between the wildcard
approach and the type-parameter approach. Returning to the example,
Optional<Integer> oi = Optional.empty();
Function<Number, Optional<StringBuilder>> fm = n -> Optional.empty();
Optional<CharSequence> ocs = oi.flatMap(fm);
If the flatMapper function itself has a wildcard type, for example,
Function<Number, Optional<? extends CharSequence>> fm = n ->
Optional.empty();
then this will still work with the wildcard approach but fail with the
type-parameter approach. As Rémi also pointed out, a wildcarded type can result
from the capture of a type with a wildcarded type parameter.
Based on this, I believe the nested wildcard approach to be the correct one.
s'marks