Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 flatMap + Optional.of doesn't compile

I'm trying to flatMap Optionals in Java. Here is a simplified example:

List<String> x = Arrays.asList("a", "b", "c");
List<String> result = x.stream().flatMap((val) -> val.equals("b") ? Optional.empty() : Optional.of(val)).collect(Collectors.toList());

I get this error message from the compiler:

Error:(10, 27) java: incompatible types: no instance(s) of type variable(s) T exist so that java.util.Optional<T> conforms to java.util.stream.Stream<? extends R>

What's wrong? Here is an example of what I'm trying to achieve in Scala:

List("a", "b", "c").flatMap(x => if (x == "b") None else Some(x))

It returns:

res2: List[String] = List(a, c)

as expected.

How do I convert this to Java so that it compiles?

like image 645
auramo Avatar asked Oct 28 '14 15:10

auramo


People also ask

What is flatMap and optional in Java programming?

Introduction Before diving deep into the practice stuff let us understand the flatMap method and Optional class in java programming. This method is used to convert a stream of collections to a stream of objects. It is an intermediate method and returns a stream as the output value.

How to use flatMap () method on a stream in Java?

We can use a flatMap () method on a stream with the mapper function List::stream. On executing the stream terminal operation, each element of flatMap () provides a separate stream. In the final phase, the flatMap () method transforms all the streams into a new stream.

Does it make sense to map to an optional in Java?

It makes total sense for Java. The class invariant is that an Optional can never hold a null value, so either the map should fail or it should return an empty Optional. The second seems the better choice. In stricter functional programming languages (aka Scala), this doesn’t happen.

What is the difference between map and flatMap?

Following are few facts about flatMap operation. Optional.flatMap (mapper) takes a mapping Function as an input, which accept reference of type T and return Optional<U>. If the value of T is null, flatMap returns empty Optional. Compared to map (Function), flatMap does not wrap it with an additional Optional.


2 Answers

flatMap is expected to map an element of the input Stream into a different Stream. Therefore it must return a Stream and not an Optional.

Therefore, you should do something like this :

List<String> x = Arrays.asList("a", "b", "c");
List<Optional<String>> result = 
    x.stream()
     .flatMap((val) -> 
                  val.equals("b") ? Stream.of(Optional.empty()) : 
                                    Stream.of(Optional.of(val)))
     .collect(Collectors.toList());

Note that if your goal is simply to get rid of some of the values ("b" in your example), you don't need to use Optional at all. You can just filter the Stream :

List<String> result = 
    x.stream()
     .filter (val -> !val.equals("b"))
     .collect(Collectors.toList());

This way you don't need flatMap and your output is a List<String> instead of a List<Optional<String>>.

As Holger commented, the solution that returns a Stream of Optionals can be simplified by using map instead of flatMap, since each element is mapped into a single Optional :

List<String> x = Arrays.asList("a", "b", "c");
List<Optional<String>> result = 
    x.stream()
     .map((val) -> val.equals("b") ? Optional.empty() : Optional.of(val))
     .collect(Collectors.toList());
like image 186
Eran Avatar answered Nov 01 '22 23:11

Eran


There is no need to deal with Optional here.

The simplest straight-forward solution is to use filter

List<String> result = x.stream()
    .filter(val -> !val.equals("b"))
    .collect(Collectors.toList());

If you insist on using flatMap, you should simply use Stream instead of Optional:

List<String> result = x.stream().flatMap(
    val -> val.equals("b")? Stream.empty(): Stream.of(val))
    .collect(Collectors.toList());

If you have to deal with an operation that unavoidably produces an Optional, you will have to convert it to a Stream for using Stream.flatMap:

List<String> result = x.stream()
    .map(val -> val.equals("b") ? Optional.<String>empty() : Optional.of(val))
    .flatMap(o->o.map(Stream::of).orElse(Stream.empty()))
    .collect(Collectors.toList());
like image 43
Holger Avatar answered Nov 01 '22 21:11

Holger