31

I have a number of functions:

String first(){}
String second(){}
...
String default(){}

Each can return a null value, except the default. each function can take different parameters. For example, first could take no arguments, second could take in a String, third could take three arguments, etc. What I'd like to do is something like:

ObjectUtils.firstNonNull(first(), second(), ..., default());

The problem is that because of the function call, this does eager evaluation. Where'd I'd like to exit early, say after the second function (because the function calls can be expensive, think API calls, etc). In other languages, you can do something similar to this:

return first() || second() || ... || default()

In Java, I know I can do something like:

String value;
if (value = first()) == null || (value = second()) == null ...
return value;

That's not very readable IMO because of all the == null checks.ObjectUtils.firstNonNull() creates a collection first, and then iterates, which is okay as long as the function gets evaluated lazily.

Suggestions? (besides doing a bunch of ifs)

lorenzocastillo
  • 985
  • 3
  • 13
  • 24

8 Answers8

46
String s = Stream.<Supplier<String>>of(this::first, this::second /*, ... */)
                 .map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElseGet(this::defaultOne);

It stops on the first non-null value or else sets the value which is returned from defaultOne. As long as you stay sequential, you are safe. Of course this requires Java 8 or later.

The reason why it stops on the first occurrence of a non-null value is due how the Stream handles each step. The map is an intermediate operation, so is filter. The findFirst on the other side is a short-circuiting terminal operation. So it continues with the next element until one matches the filter. If no element matches an empty optional is returned and so the orElseGet-supplier is called.

this::first, etc. are just method references. If they are static replace it with YourClassName::first, etc.

Here is an example if the signature of your methods would differ:

String s = Stream.<Supplier<String>>of(() -> first("takesOneArgument"),
                                       () -> second("takes", 3, "arguments")
                                   /*, ... */)
                 .map(Supplier::get)
                 .filter(Objects::nonNull)
                 .findFirst()
                 .orElseGet(this::defaultOne);

Note that the Supplier is only evaluated when you call get on it. That way you get your lazy evaluation behaviour. The method-parameters within your supplier-lambda-expression must be final or effectively final.

Roland
  • 22,259
  • 4
  • 57
  • 84
  • 1
    How would this look if the methods each take different parameters? – lorenzocastillo Apr 07 '17 at 14:22
  • how do they differ? – Roland Apr 07 '17 at 14:23
  • 1
    This is an example of how using the latest "tool" can sometimes be a hinderance. In C++ they taught operator overloading last, and everyone ruined the language by implementing them where they were not required. This creates a lot of intermediate objects to handle the stream processing, prevents the addition of debugging statements, and is not particularly easier to read than a simple for loop. Don't get me wrong, I like Lambdas, but if I wanted to program everything in Lambdas, I'd use Lisp. – Edwin Buck Apr 07 '17 at 14:26
  • Say first(String a), second(int x), third(String a, double y) – lorenzocastillo Apr 07 '17 at 14:26
  • 2
    @EdwinBuck why a hindrance? This is a clean example of Stream being using for what it's good at. – slim Apr 07 '17 at 14:28
  • @EdwinBuck Allow me to kindly disagree... While it might be true that intermediate objects are created, it is false that you cannot add debugging statements (see `Stream.peek` method). Besides, sometimes object creation is not a problem, if at the end your code is clear and more maintainable (which is debatable, I admit). – fps Apr 07 '17 at 14:30
  • @slim It is not a hinderance in ability to perform the task. It is a hinderance in ability to maintain the solution. Object-oriented was created in an attempt to make C code more maintainable. Looking at your Stream, it seems that it is nearly as long as the five line for loop it would replace. But the for loop would permit adding in debugging statements (especially conditional ones), often a need in maintaining code (logging runtime error conditions post-deployment, etc). It's not the code is bad, it's just that Lambda is somewhat at odds with OO. – Edwin Buck Apr 07 '17 at 14:33
  • 1
    @lorenzocastillo "How would this look if the methods each take different parameters?" In my opinion, you should clarify you want an answer for this in the question. Please edit it so that the answer is valid and both your questions find an answer. – Olivier Grégoire Apr 07 '17 at 14:34
  • 1
    @TedHopp `findFirst` is a short-circuiting operation – fps Apr 07 '17 at 14:38
  • @TedHopp see the end of my own answer. `findFirst` is in charge; it stops asking for items when it sees a result. The rest of the supply chain works lazily. – slim Apr 07 '17 at 14:39
  • @FedericoPeraltaSchaffner Disagreement is the spice of life. I've flipped on my stance about this idea a few times. But, when I see a Lambda, what I often fail to see is the addition of nouns and verbs (expressiveness) in modeling the solution. Too many Lambdas without wrapping them in methods often fails to express the intent of why they were written, which is far more important than what any line of code (in any approach) does mechanically. – Edwin Buck Apr 07 '17 at 14:39
  • @EdwinBuck (must avoid discussion here) -- verbs like "map", "filter", "find"? – slim Apr 07 '17 at 14:41
  • @EdwinBuck I totally agree with you on this one: inlining all your flow is far from being readable or maintainable. I find it that managing flow with streams and optionals is good, as long as actions are delegated to (maybe) private methods. But this is just my personal taste. Anyway, nothing new here: one action, one method, which is 100% OO stuff. – fps Apr 07 '17 at 14:44
  • @slim Yes, they're verbs, but on the wrong objects. Streams being able to do those things is how it is done. Basically it is behavior in the stream, across data somewhere else. It is a very effective pattern, and essential for some workflows, but not a very object-oriented approach. – Edwin Buck Apr 07 '17 at 14:45
  • @Roland why do you need `Supplier` here? Why don't simply use `Stream.of(this::first, this::second /*, ... */)...` – Andriy Kryvtsun Apr 07 '17 at 14:57
  • 2
    @AndriyKryvtsun we want lazy evaluation and the supplier ensures that the evaluation only takes place when we call `get` on it. If you would write `Stream.of(first(), second())` then both methods would be executed before the `Stream` is even constructed. – Roland Apr 07 '17 at 15:00
11

This can be done pretty cleanly with a stream of Suppliers.

Optional<String> result = Stream.<Supplier<String>> of(
     () -> first(), 
     () -> second(),
     () -> third() )
  .map( x -> x.get() )
  .filter( s -> s != null)
  .findFirst(); 

The reason this works is that despite appearances, the whole execution is driven by findFirst(), which pulls an item from filter(), which lazily pulls items from map(), which calls get() to handle each pull. findFirst() will stop pulling from the stream when one item has passed the filter, so subsequent suppliers will not have get() called.

Although I personally find the declarative Stream style cleaner and more expressive, you don't have to use Stream to work with Suppliers if you don't like the style:

Optional<String> firstNonNull(List<Supplier<String>> suppliers {
    for(Supplier<String> supplier : suppliers) {
        String s = supplier.get();
        if(s != null) {
            return Optional.of(s);
        }
    }
    return Optional.empty();
}

It should be obvious how instead of returning Optional you could equally return a String, either returning null (yuk), a default string, or throwing an exception, if you exhaust options from the list.

slim
  • 40,215
  • 13
  • 94
  • 127
3

It isn't readable because you are dealing with a bunch of separate functions that don't express any kind of connection with each other. When you attempt to put them together, the lack of direction is apparent.

Instead try

 public String getFirstValue() {
      String value;
      value = first();
      if (value != null) return value;
      value = second();
      if (value != null) return value;
      value = third();
      if (value != null) return value;
      ...
      return value;
 }

Will it be long? Probably. But you are applying code on top of a interface that's not friendly toward your approach.

Now, if you could change the interface, you might make the interface more friendly. A possible example would be to have the steps be "ValueProvider" objects.

public interface ValueProvider {
    public String getValue();
}

And then you could use it like

public String getFirstValue(List<ValueProvider> providers) {
    String value;
    for (ValueProvider provider : providers) {
       value = provider.getValue();
       if (value != null) return value;
    }
    return null;
}

And there are various other approaches, but they require restructuring the code to be more object-oriented. Remember, just because Java is an Object-Oriented programming language, that doesn't mean it will always be used in an Object-Oriented manner. The first()...last() method listing is very not-object oriented, because it doesn't model a List. Even though the method names are expressive, a List has methods on it which permit easy integration with tools like for loops and Iterators.

Edwin Buck
  • 69,361
  • 7
  • 100
  • 138
  • 4
    Another suitable name for `ValueProvider` is `Supplier`. Or you could use the one that's already in the JRE. `Supplier` – slim Apr 07 '17 at 14:54
  • @slim Possibly, but without knowledge of the actual problem domain, all the names are going to be sub-optimal. – Edwin Buck Apr 07 '17 at 15:03
2

If you are using java 8 you can convert these function calls to lambdas.

public static<T> T firstNonNull(Supplier<T> defaultSupplier, Supplier<T>... funcs){
    return Arrays.stream(funcs).filter(p -> p.get() != null).findFirst().orElse(defaultSupplier).get();
}

If you don't want the generic implementation and use it only for Strings go on and just replace T with String:

public static String firstNonNull(Supplier<String> defaultSupplier, Supplier<String>... funcs){
    return Arrays.stream(funcs).filter(p -> p.get() != null).findFirst().orElse(defaultSupplier).get();
}

And then call it like:

firstNonNull(() -> getDefault(), () -> first(arg1, arg2), () -> second(arg3));

P.S. btw default is a reserved keyword, so you cannot use it as a method name :)

EDIT: ok, the best way to do this would be to return Optional, then you don't need to pass default supplier separetely:

@SafeVarargs
public static<T> Optional<T> firstNonNull(Supplier<T>... funcs){
    return Arrays.stream(funcs).filter(p -> p.get() != null).map(s -> s.get()).findFirst();
}
Nestor Sokil
  • 2,162
  • 12
  • 28
2

If you want to package it up into a utility method, you'll have to wrap each function up into something that defers execution. Perhaps something like this:

public interface Wrapper<T> {
    T call();
}

public static <T> T firstNonNull(Wrapper<T> defaultFunction, Wrapper<T>... funcs) {
    T val;
    for (Wrapper<T> func : funcs) {
       if ((val = func.call()) != null) {
           return val;
       }
    }
    return defaultFunction.call();
}

You could use java.util.concurrent.Callable instead of defining your own Wrapper class, but then you'd have to deal with the exception that Callable.call() is declared to throw.

This can then be called with:

String value = firstNonNull(
    new Wrapper<>() { @Override public String call() { return defaultFunc(); },
    new Wrapper<>() { @Override public String call() { return first(); },
    new Wrapper<>() { @Override public String call() { return second(); },
    ...
);

In Java 8, as @dorukayhan points out, you can dispense with defining your own Wrapper class and just use the Supplier interface. Also, the call can be done much more cleanly with lambdas:

String value = firstNonNull(
    () -> defaultFunc(),
    () -> first(),
    () -> second(),
    ...
);

You can also (as @Oliver Charlesworth suggests) use method references as shorthand for the lambda expressions:

String value = firstNonNull(
    MyClass::defaultFunc,
    MyClass::first,
    MyClass::second,
    ...
);

I'm of two minds as to which is more readable.

Alternatively, you can use one of the streaming solutions that many other answers have proposed.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • You cannot do in line || in Java if the functions don't return booleans – lorenzocastillo Apr 07 '17 at 14:36
  • @lorenzocastillo - D'oh. Yeah, that first option isn't going to work. Too much mixed Java and JavaScript programming. :( – Ted Hopp Apr 07 '17 at 14:37
  • You ought to be able to replace the lambdas with method references in the last snippet. – Oliver Charlesworth Apr 07 '17 at 15:43
  • `java.util.function.Supplier` is a much better alternative to `java.util.concurrent.Callable`. The only difference is that its only method is called "get" instead of "call". – The SE I loved is dead Apr 08 '17 at 16:36
  • @dorukayhan - Good point. I'll update the Java 8 part of the answer. (There's another difference: `Supplier.get()` isn't declared to throw `Exception`, so it's actually easier to use than `Callable.call()`.) – Ted Hopp Apr 09 '17 at 00:52
0

Just make a class with one function like this:

class ValueCollector {
  String value;
  boolean v(String val) { this.value = val; return val == null; }
}

ValueCollector c = new ValueCollector();
if c.v(first()) || c.v(second()) ...
return c.value;
DenisSt
  • 21
  • 3
0

The above examples seemed too long for just choosing between 2 variables, I'd go with something like this (unless you've got a longer list of variables to chose from):

Optional.ofNullable(first).orElse(Optional.ofNullable(second).orElse(default));

Tatiana Goretskaya
  • 536
  • 10
  • 25
-3

You can accomplish this via reflection:

public Object getFirstNonNull(Object target, Method... methods) {
    Object value = null;
    for (Method m : methods) {
        if ( (value = m.invoke(target)) != null) {
            break;
        }
    }
    return value;
}
Ryan S
  • 370
  • 2
  • 12