Search code examples
javalambdajava-8option-typeflatmap

JAVA8 Optional and Lambdas


Suppose I have this class model hierarchy:

public class A {
 private Integer id;
 private List<B> b;
}

And:

public class B {
 private Integer id;
 private List<C> c;
}

And finally:

public class C {
 private Integer id;
}

And a simple Service:

@Service
public class doSome {
  
  public void test() {
    Optional<A> a = Optional.of(a) // Suppose a is an instance with full hierarchy contains values
    /** *1 **/               // what I want to do
  }
}

Now what I want to do at the *1 position is to use lambda to extract the Optional value (if exixsts) and map the subrelation to obtain all id of the C class. I have tried something like this:

  public void test() {
    Optional<A> a = Optional.of(a);
    List<Integer> temp = a.get().getB()
                                .stream()
                                .map(x -> x.getC())
                                .flatMap(List::stream)
                                .map(y -> y.getId())
                                .collect(Collectors.toList());   // works
  }

Now I would like to put inside my lambda the a.get().getB(), I have tried several ways but with no luck.

Anyway I don't understand why I can't use two consecutive map like

 .map(x -> x.getC())
 .flatMap(List::stream)
 .map(y -> y.getId())

without using flatMap(List::stream) in the middle... the map doesn't return a new Stream of Type R (class C in this case)? Why I have to Stream it again? where am I wrong?

----------------------- UPDATE ------------------

This is just an example, It's pretty clear that the Optional here is useless but in real case could comes by a findById() JPA Query.

Holger for this reasons I would put all inside a part of code, doing something like:

public <T> T findSome(Integer id) {
  Optional<T> opt = repository.findById(id);
  return opt.map(opt -> opt).orElse(null);
}

I have read here some solution like follows:

Optional.ofNullable(MyObject.getPeople())
  .map(people -> people                                                                    
    .stream()                                                                    
    .filter(person -> person.getName().equals("test1"))
    .findFirst()
    .map(person -> person.getId()))
  .orElse(null);

And I would like to adapt at my case but without no luck.


Solution

  • As of and newer, you can call Optional#stream:

    List<Integer> temp = a.map(A::getB)
            .stream()
            .flatMap(Collection::stream)
            .map(B::getC)
            .flatMap(Collection::stream)
            .map(C::getId)
            .collect(Collectors.toList());
    

    If you are stuck with , you need to map to Stream (or return the empty one) and continue chaining:

    List<Integer> temp = a.map(A::getB)
            .map(Collection::stream)
            .orElse(Stream.empty())
            .map(B::getC)
            .flatMap(Collection::stream)
            .map(C::getId)
            .collect(Collectors.toList());
    

    Note: Optional<A> a = Optional.of(a) is not valid as a is already defined.