8

I'm moving some code to java8, trying (sometimes forcing myself) to use of streams and lambdas, and I'm not comfortable yet with them.

I have some methods in a class that validate a business object. Every method looks like

Optional<Fail> validate1(BusinessObject bo)

where Fail is an enum that somehow describes the error, and if there's no error the method returns Optional.empty(). I don't need to collect all errors, but return the first error, without executing following validations.

What I'm doing is

//first convert methods to suppliers
Supplier<Optional<Fail>> validate1= () -> validate1(bo);
Supplier<Optional<Fail>> validate2= () -> validate2(bo);
Supplier<Optional<Fail>> validate3= () -> validate3(bo);
//then some stream magic
return Stream.of(validate1, validate2, validate3)
    .map(Supplier::get)
    .filter(f -> f.isPresent())
    .findFirst()
    .orElse(Optional.empty()); //without the orElse, no error would return
                                         // Optional(Optional.empty()) 
                                         // instead of Optional.empty()

It works, it does the job, it does not execute unnecessary methods, it's legible (it'd be more legible if Optional.orElse were named getOrElse, but that's out of my reach). What I'm trying to find out is if this is a reasonable way to do what I want, if this code would be considered 'good style' or 'idiomatic java8', or am I misusing Stream or Optional, or missing something obvious.

The idea of returning the first non-empty Optional or an empty Optional if they are all empty looks general enough to think there is an official way to do it, something in the back of my head is yelling 'Monads!', but my ignorance of Haskell is almost perfect, so I don't know.

Pablo Grisafi
  • 5,039
  • 1
  • 19
  • 29
  • Looks like quite reasonable code style. – Tagir Valeev Jun 17 '15 at 02:48
  • If I were you I would probably do `Stream.of(validate1(bo), validate1(bo), validate1(bo))` rather than use a supplier. – Jatin Jun 17 '15 at 04:28
  • 1
    @Jatin that would call all validate methods immediately. OP wants to only call them as needed. – Misha Jun 17 '15 at 04:49
  • @Misha Oops. Yes. A very valid point. – Jatin Jun 17 '15 at 05:04
  • See [this answer](http://stackoverflow.com/a/28833677/2711488); it’s basically the same as the code of your question, but more concise and by using `.map(Optional::get)` before `.findFirst()` you can save the `orElse` part… – Holger Jun 17 '15 at 07:44

1 Answers1

3

Optional is a lot like a Stream with 0 or 1 elements in it. Yet it doesn't implement Stream, nor has it a stream() method (like collections do).

However, it's not that hard converting an Optional<T> to a Stream<T>, this Function does it :

public static <T> Function<Optional<? extends T>, Stream<T>> asStream() {
    return op -> op.map(Stream::of).orElseGet(Stream::empty);
}

With that method available you can simply use flatMap :

Stream.of(validate1, validate2, validate3)
        .map(Supplier::get)
        .flatMap(asStream())
        .findFirst();
bowmore
  • 10,842
  • 1
  • 35
  • 43
  • 2
    Java 9 [will have](http://download.java.net/jdk9/docs/api/java/util/Optional.html#stream--). Nevertheless, `.flatMap(asStream())` or Java 9’s `.flatMap(Optional::stream)` isn’t that different to `.filter(Optional::isPresent).map(Optional::get)`. It doesn’t answer the OP’s question about the validity of the concept in general… – Holger Jun 17 '15 at 10:24