Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

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)

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
172 views
Welcome To Ask or Share your Answers For Others

1 Answer

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...