Search code examples
javamethodsjava-8java-streamoption-type

Whats the difference between Optional.flatMap() and Stream.flatMap()


What is the difference between Optional.flatMap() and Stream.flatMap().

Correct flatMap on Stream:

    List<ObjectDTO> collect = types.stream()
            .flatMap(a -> client.getSthById(new URI(a)).stream())
            .collect(Collectors.toList());

Exception while using flatMap on Optional:

        List<ObjectDTO> collect2 = client.getSthByObj(obje.get(), null).getBrowse()
                .flatMap(uri -> client.getSthById(uri).stream())
                .collect(Collectors.toList());

Why can't I use it in the same way?


Solution

  • It’s hard to be sure from your code snippets where we don’t know the types of the variables you use nor the return types of the methods. But I think that the source of your error is that you are trying to pass a lambda that returns a Stream to Optional.flatMap().

    Let’s look at Stream first. Stream.flatMap() takes a function that returns a Stream. This seems to be exactly what you are giving it in your first code snippet.

    Optional on the other hand: Optional.flatMap() requires a function that returns an Optional. If getBrowse() returns an Optional, then what you pass to Optional.flatMap is uri -> physicalInventoryClient.getSublocationsByIds(uri).stream(). This looks like your lambda returns a stream, not an Optional. When I try the same in my Eclipse, I get a compile error like the following:

    The method flatMap(Function<? super String,? extends Optional<? extends U>>) in the type Optional<String> is not applicable for the arguments ((Object x) -> {})

    The solution? From Java 9 Optional has a stream method that will probably let you achieve what you are trying. Again, without knowing your code it’s hard to suggest, but probably something like:

        List<SublocationBrowseDTO> collect2 = physicalInventoryClient.getSublocationsByLocation(masterLocation.get(), null)
                .getBrowse()
                .stream()
                .flatMap(uri -> physicalInventoryClient.getSublocationsByIds(uri).stream())
                .collect(Collectors.toList());
    

    Edit: Alternative without stream:

        List<SublocationBrowseDTO> collect2 = physicalInventoryClient.getSublocationsByLocation(masterLocation.get(), null)
                .getBrowse()
                .map(uri -> physicalInventoryClient.getSublocationsByIds(uri))
                .orElse(Collections.emptyList());
    

    The latter version requires that getSublocationsById() returns a list, but can probably be modified to work if the return type is something else. IMHO it’s a bit simpler.