1

How can I use stream to evaluate multiple service calls only if the previous call returned null? ie.

Stream.of(service1(), service2(), service3())
    .filter(Objects::nonNull)
    .findFirst()
    .ifPresent(this::doSomething);

basically I don't want all three service calls to be invoked if they don't have to be, and I'm interested if there's a better way to do this without a bunch of

if(service1() != null)
...
else if(service2() != null)
...
else if(service3() != null)
...
shmosel
  • 49,289
  • 6
  • 73
  • 138

2 Answers2

2

Assuming that each of service1(), service2(), etc. return the same datatype, then you can provide a lambda expression that calls each service and returns that type -- a Supplier<Result>, with Result being that datatype. You can alternatively supply method references.

Stream.<Supplier<Result>>of(YourService::service1, YourService::service2, YourService::service3)

Streams will evaluate lazily, so using method references allows you take advantage of this and defer execution until needed.

    .map(supplier -> supplier.get())

The rest of your execution chain can work the way you have it.

    .filter(Objects::nonNull)
    .findFirst()
    .ifPresent(this::doSomething);
rgettman
  • 176,041
  • 30
  • 275
  • 357
1

Here's a complete example:

public class Main {
    public static void main(String[] args) {
        Stream<Supplier<String>> suppliers =
            Stream.of(() -> service1(), () -> service2(), () -> service3(), () -> service4());

        suppliers.map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .ifPresent(System.out::println);
    }

    private static String service1() {
        System.out.println("service1");
        return null;
    }

    private static String service2() {
        System.out.println("service2");
        return null;
    }

    private static String service3() {
        System.out.println("service3");
        return "hello";
    }

    private static String service4() {
        System.out.println("service4");
        return "world";
    }
}
JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • 1
    very cool! I wrote up a very similar example to test this and it works great. I'm going to mark this as the answer and thanks!! – Daniel Spence Dec 21 '17 at 22:41
  • @JB Nizet, how can we be sure that map won't be called for all suppliers? – tsolakp Dec 21 '17 at 22:51
  • 1
    We can be sure because findFirst() is a short-cicuiting terminal operation: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html#findFirst-- – JB Nizet Dec 21 '17 at 22:53
  • Thanks, but it does not say anything about not processing some elements before terminating. – tsolakp Dec 21 '17 at 23:08
  • If it processed all the elements before terminating, it wouldn't be short-circuiting anymore, would it? – JB Nizet Dec 21 '17 at 23:13
  • so.. what if I said those services can throw exceptions..? This doesn't seem to work in that case. – Daniel Spence Dec 21 '17 at 23:20
  • Nevermind. I answered my own question. I created a functional interface called ExSupplier that can throw Exception and used that. Thanks again. – Daniel Spence Dec 21 '17 at 23:38