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.