Java 8 flatMap + Optional.of doesn't compile
Asked Answered
A

2

6

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?

Assam answered 28/10, 2014 at 15:14 Comment(0)
P
2

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());
Percent answered 28/10, 2014 at 17:10 Comment(0)
L
6

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());
Lysenkoism answered 28/10, 2014 at 15:21 Comment(6)
Thanks! Normally I'd filter. In this case (the real case, not the simplified toy example) I'd rather use Optionals because filtering would mean digging through a lot of crap which I'll also have to do in the map-phase.Assam
When every element of the stream is mapped to exactly one Optional, there is no need to use flatMap. Just use .map(val -> val.equals("b")? Optional.empty(): Optional.of(val))Percent
@Percent You are absolutely right. I didn't think about it. I was following the OP's decision to use a flatMap.Lysenkoism
@Percent If I just use .map instead of .flatMap I get List<Optional<String>> instead of the desired List<String>. And there is no flatten like in scala either :( So what's the easiest way to get to List<String>? I know I can filter and then map again, but it starts to be way too verbose...Assam
Yeah, and I said why I'd like to use Optional in my first comment. In this toy example, yes filter would be the way to go. But I had a real-world case where optional would have been better (but only in Scala it seems, Java doesn't seem to cut it)Assam
Stream.of and Stream.empty are just what I wanted! Thanks.Assam
P
2

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());
Percent answered 28/10, 2014 at 17:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.