Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an elegant way to get the first non null value of multiple method returns in Java?

You have already seen this many times yourself, of that I'm sure:

public SomeObject findSomeObject(Arguments args) {
    SomeObject so = queryFirstSource(args); // the most likely source first, hopefully
    if (so != null) return so;

    so = querySecondSource(args); // a source less likely than the first, hopefully
    if (so != null) return so;

    so = queryThirdSource(args); // a source less likely than the previous, hopefully
    if (so != null) return so;

    // and so on
}

We have different sources where an object we search could be. As a more vivid example we could image that we first check if a userid is in a list of privileged users. If not we check if the userid is in the list of allowed users. Else we return null. (It's not the best example but I hope it's a vivid-enough one.)

Guava offers us some helpers that could beautify that code above:

public SomeObject findSomeObject(Arguments args) {
    // if there are only two objects
    return com.google.common.base.Objects.firstNonNull(queryFirstSource(args), querySecondSource(args));

    // or else
    return com.google.common.collect.Iterables.find(
        Arrays.asList(
            queryFirstSource(args)
            , querySecondSource(args)
            , queryThirdSource(args)
            // , ...
        )
        , com.google.common.base.Predicates.notNull()
    );
 }

But, as the more experienced among us will have already seen, this may perform bad if the lookups (i.e. queryXXXXSource(args)) take a certain amount of time. This is because we now query all sources first and then pass the results to the method that finds the first among those results which is not null.

In contrast to the first example, where the next source is only evaluated when the former does not return something, this second solution may look better at first but could perform much worse.

Here's where we come to my actual question and to where I suggest something of that I hope someone has already implemented the base of it or of that someone might propose a even smarted solution.

In plain English: Has someone already implemented such a defferedFirstNonNull (see below) or something similar? Is there an easy plain-Java solution to achieve this with the new Stream framework? Can you propose another elegant solution that achieves the same result?

Rules: Java 8 is allowed as well as active maintained and well-known third party libraries like Google's Guava or Apache's Commons Lang with Apache License or similar (No GPL!).

The proposed solution:

public SomeObject findSomeObject(Arguments args) {
    return Helper.deferredFirstNonNull(
        Arrays.asList(
            args -> queryFirstSource(args)
            , args -> querySourceSource(args)
            , args -> queryThirdSource(args)
        )
        , x -> x != null
     )
 }

So the method defferedFirstNonNull would evaluate each lambda expression after another and as soon as the predicate (x -> x != null) is true (i.e. we found a match) the method would return the result immediately and would not query any further source.

PS: I know that the expressions args -> queryXXXXSource(args) could be shortened to queryXXXXSource. But that would render the proposed solution harder to read because it's not obvious on first sight what is going to happen.

like image 258
cimnine Avatar asked Sep 30 '14 10:09

cimnine


People also ask

How do I get the first NOT NULL value in SQL?

SQL COALESCE – a function that returns the first defined, i.e. non-NULL value from its argument list. Usually one or more COALESCE function arguments is the column of the table the query is addressed to. Often a subquery is also an argument for a function.

How do I avoid multiple nulls in Java 8?

We can get rid of all those null checks by utilizing the Java 8 Optional type. The method map accepts a lambda expression of type Function and automatically wraps each function result into an Optional . That enables us to pipe multiple map operations in a row.

What is coalesce in Java?

A coalesce expression is equivalent to a case expression that returns null if all its arguments evaluate to null, and the value of its first non-null argument otherwise.

Why is checking for null a good practice?

It is a good idea to check for null explicitly because: You can catch the error earlier. You can provide a more descriptive error message.


2 Answers

Yes, there is:

Arrays.asList(source1, source2, ...)
   .stream()
   .filter(s -> s != null)
   .findFirst();

This is more flexible, since it returns an Optional not null in case a not-null source is found.

Edit: If you want lazy evaluation you should use a Supplier:

Arrays.<Supplier<Source>>asList(sourceFactory::getSource1, sourceFactory::getSource2, ...)
   .stream()
   .filter(s -> s.get() != null)
   .findFirst();
like image 61
Random42 Avatar answered Sep 18 '22 23:09

Random42


I would write it like this (you may not need generics here but why not do it):

public static <A, T> Optional<T> findFirst(Predicate<T> predicate, A argument,
                                         Function<A, T>... functions) {
  return Arrays.stream(functions)
          .map(f -> f.apply(argument))
          .filter(predicate::test)
          .findFirst();
}

And you can call it with:

return findFirst(Objects::nonNull, args, this::queryFirstSource,
                                       this::querySecondSource,
                                       this::queryThirdSource);

(assuming your queryXXX methods are instance methods)

The methods will be applied in order until one returns a value that matches the predicate (in the example above: returns a non null value).

like image 21
assylias Avatar answered Sep 18 '22 23:09

assylias