Find the bug in this Optional chain
This chain is meant to look up a client and build a response. It does not compile. Infer the type after each step, say whether the fault is at compile time or runtime, and fix it.
Constraints:
getSessionInforeturnsSessionInfo;findClientreturnsOptional<Client>createResponse(Client)takes aClient, not anOptional
return Optional.of(request)
.map(sessionService::getSessionInfo) // Optional<SessionInfo>
.map(authService::findClient) // findClient returns Optional<Client>
.map(client -> createResponse(client)) // expects a Client here
.orElse(noClientResponse());
Find and fix the bug.
It is a compile-time error, not runtime. map(authService::findClient) wraps the already-Optional result, producing Optional<Optional<Client>>. So in the next map, client is an Optional<Client>, not a Client, and passing it to createResponse(Client) fails to type-check. Fix it by using flatMap for findClient: flatMap unwraps one level, yielding Optional<Client> so the next map receives a real Client.
- ✗Calling the error a runtime fault when it is caught at compile time
- ✗Missing that
mapover anOptional-returning function nests toOptional<Optional<T>> - ✗Reaching for
.get()instead offlatMapto flatten one level
- →When do you use
mapversusflatMapon anOptional? - →Why is
Optional<Optional<T>>almost always a code smell?
Walkthrough
return Optional.of(request)
.map(sessionService::getSessionInfo) // Optional<SessionInfo>
.flatMap(authService::findClient) // was map → Optional<Optional<Client>>; now flatMap → Optional<Client>
.map(client -> createResponse(client)) // client is now a Client
.orElse(noClientResponse());
The type after each step:
Optional.of(request)→Optional<OnStartUpRequest>.map(getSessionInfo)→Optional<SessionInfo>.map(findClient)→Optional<Optional<Client>>— becausefindClientitself returns anOptional, andmapwraps the result once more- the next
.map(client -> ...)receivesclientof typeOptional<Client>, butcreateResponseexpects aClient→ compile error
It is a compile-time error: the compiler cannot match the argument type, so it never reaches execution.
The fix is to replace map with flatMap for findClient. flatMap flattens one level of nesting: instead of Optional<Optional<Client>> you get Optional<Client>, and the next map receives a real Client.