How to properly use the combination of .map and .orElseGet within stream API?

2.6k Views Asked by At

I have the following code:

private List<User> findUsers(...) {
...

    return usersData.stream() //userData is another list of objects
        .findFirst()
        .map(this::getCode)
        .map(code-> {
            if (...) {
                Optional<AnotherObject> anotherObject = getAnotherObject();
                return anotherObject.map(userService::getUsersFromAnotherObject) // getUsersFromAnotherObject returns List<User> but the whole line returns Optional of List<User>
            } else {
                ...
                return null;
            }
        }).orElseGet(() -> findXYZ(...));
}

which does not compile and says:
"Bad return type in lambda expression: List<User> cannot be converted to Optional<List<User>>"
even though findXYZ and all other if/else statements return in fact type List.

Could anybody explain to me what is wrong with the code?

EDIT: Sorry, I noticed that one of if statements is actually returning the Optional of List

If anybody is interested, I solved it simply editing the first "if" to:

return userService.getUsersFromAnotherObject(anotherObject.orElse(null));
2

There are 2 best solutions below

3
On BEST ANSWER

(edited)

In your case, the return of the two should match because that's the use of the orElseGet() - to give an alternate value of the same type.

.map(code -> return ...)
.orElseGet(() -> return ...)

There are two options in your case:

  1. since your map() returns Optional<List<User>>, you can update the findXyz() to return the same

  2. update code of map() to something like below (return List<User> without Optional wrap, then you can keep your findXyz() in it's original).

         usersData.stream().findFirst()
         .map(this::getCode)
         .map(code-> {
             if (...) {
                 Optional<AnotherObject> anotherObject = getAnotherObject();
                 Optional<List<User>> optUserList = anotherObject.map(userService::getUsersFromAnotherObject)
                 return optUserList.isPresent() ? optUserList.get() : null;
             } else {
                     ...
                 return null;
             }
         }).orElseGet(() -> findXYZ(...));`
    
1
On

Just add a .get() after orElseGet()

private List<User> findUsers(...)
{
    ...
    return usersData.stream() //userData is another list of objects 
           .findFirst()
           .map(this::getCode)
           .map(code-> {
               if (...) {
                   return ...
               }
               else {
                   return ...;
               }
           })
           .orElseGet(() -> findXYZ(...))
           .get(); 
}

Because .orelse get return object of Optional class