SQL gives us a “coalesce” function, which returns the first non-null argument.
This seems to be a common operation in Java too, since Java unfortunately burdens us with the concept of nulls. We have been able to do something similar with Java for some time using a varargs method like this:
public static T coalesce(T... ts) {
for (T t : ts)
if (t != null)
return t;
return null;
}
String result = coalesce(somethingPossiblyNull(), somethingElse(), "defaultValue");
(There is a similar method on Guava’s Objects class);
It looks nice and avoids the need for ugly ifs in some places. However, unfortunately it means that somethingElse() would have to be evaluated even if somethingPossiblyNull() returned a non-null value. This is not what we want if these are expensive, so we had to fall back to something less clean.
In Java 8 thanks to lambdas & method references we can do this lazily.
public static T coalesce(Supplier... ts) {
return asList(ts)
.stream()
.map(t -> t.get())
.filter(t -> t != null)
.findFirst()
.orElse(null);
}
@Test
public void should_return_first_non_null_value() {
Person nullName = new Person(null);
Person bob = new Person("bob");
Person barbara = new Person("barbara");
assertEquals("bob", coalesce(nullName::name, bob::name, barbara::name));
}
Here we pass in suppliers for the values rather than the values themselves. First we invoke the supplier to get the value, then filter it out if it is null (as we are looking for the first non-null value), and then return the first matching – meaning that we do not look farther through the list of passed values than we need to.
We can demonstrate that we do not invoke unnecessary methods
@Test
public void should_be_lazy() {
Person bob = new Person("bob");
Person angryPerson = new Person("angry") {
@Override public String name() {
fail("Should not have asked for the angry person's name");
return "angry";
}
};
assertEquals("bob", coalesce(bob::name, angryPerson::name));
}
Here we never invoke the name method on angryPerson because bob had a non-null name.
If we want to do something more complicated than calling a Supplier-like method we can always use lambdas
@Test
public void should_be_able_to_use_lambdas() {
assertEquals("bob", coalesce(() -> new Person("bob").name(), () -> new Person("barbara").name()));
}
Or, if people ever stop returning nulls all over the place then you can of course do the same with Optionals
The AnotherSupplier interface is just to work around Type Erasure terribleness.
@Test
public void should_be_able_to_use_optionals() {
assertEquals("bob",
coalesce(
() -> Optional.empty(),
() -> Optional.of(new Person("bob").name()),
() -> Optional.of(new Person("barbara").name())
).get()
);
}
interface AnotherSupplier extends Supplier {}
public static Optional coalesce(AnotherSupplier>... ts) {
return asList(ts)
.stream()
.map(t -> t.get())
.filter(t -> t.isPresent())
.findFirst()
.orElse(Optional.empty());
}
By the way – C# provides an operator for doing this