Java 8 Lambda Chaining - Type Safety Enforcement
Snippet 1:
Optional.of(s).map(str -> str).orElse("");
Compiles because the default value provided to orElse
is the same type as the value the Optional
contains i.e. a String
.
Snippet 2:
Optional.of(s).map(str -> str).orElse(Optional.empty());
does not compile because after map
you have a Optional<String>
but then you're providing a Optional<String>
in the orElse
whereas it should be a String
.
Snippet 3:
Optional.of(s).map(str -> Optional.of(str)).orElse("hello");
does not compile because after map
you have a Optional<Optional<String>>
but you're passing a String
in the orElse
whereas it should be a Optional<String>
.
To conclude orElse
is declared as:
public T orElse(T other)
and documented as:
Returns the value if present, otherwise return other.
i.e. orElse
basically says "give me the value the optional contains if present otherwise take the default value" as well as that T
must be the same type as the value the Optional
contains.
so if you have a Optional<String
then you must supply a String
to orElse
, if you have a Optional<Integer
then you must supply a Integer
to orElse
etc...
On another note, the map
function in your first and second example snippets are superfluous and you can, therefore, omit it completely.
Whenever you see your self calling Optional#map
with the function as v -> v
it's probably not needed.
Breaking down Snippet 2:
Optional.of(s) //Optional<String>
.map(str -> str) //Optional<String>
.orElse(Optional.empty()); //String or Optional<String>?
And Snippet 3:
Optional.of(s) //Optional<String>
.map(str -> Optional.of(str)) //Optional<Optional<String>>
.orElse("hello"); //Optional<String> or String?
Now, for Snippet 3, using flatMap
can be used to get rid of the nested optionals:
Optional.of(s) //Optional<String>
.flatMap(str -> Optional.of(str)) //Optional<String>
.orElse("hello"); //String
.orElse()
attempts to repackage the Optional
, and if nothing is found, provide a default value, hence the object passed to .orElse()
needs to be compatible with what Optional is holding at the moment.
In other words, if you have an Optional<T>
, you need to pass T
to the orElse()
method.
In this case, you start with Optional<String
and then you derive Optional<Optional<String>>
from it:
Optional.of(s)
.map(str -> Optional.of(str))
.orElse("hello");
If you pass str -> str
to the map(...)
, it will compile.