slashms slashms - 4 years ago 113
Java Question

Avoid branching of null check with Optionals

Let's say I have the next piece of code:

void doSmtng(A a){
if (a!=null){
B b = a.getB();
if (b != null){
C c = b.getC();
if (c != null){
d = c.getD();
}
}
}
doSmtngWithD(d);
}


And let's say I can change the API that instead of getting
A
,
B
,
C
,
D
I can get
Optional<A>
,
Optional<B>
and so on.

What is the right way to write a more aesthetic code that will prevent all those null checks?
Also, is there a way to do what I asked with the addition of throwing an exception once one of variables (a,b,c,d..) are null?

Answer Source

Another Optional way, which was also mentioned by Holger in the comments:

D d = Optional.ofNullable(a)
              .map(A::getB)
              .map(B::getC)
              .map(C::getD)
              .orElseThrow(/* your exception */);

This can be applied to your code as is. You do not need to rewrite your accessor methods. The exception is thrown as soon as the first null value is returned. But when the exception is thrown you don't know which one exactly is null.

If you really want to know that, you are probably better of implementing a simple method, which just does your null-checks instead of using Optional, e.g.

static <T> T mapIfNotNull(T value) {
  if (value == null) {
    // throw your exception...
  }
  return value;
}

and use it without your conditions as follows:

A a = ...
B b = mapIfNotNull(a.getB());
C c = mapIfNotNull(b.getC());
D d = mapIfNotNull(c.getD());

This way you are able to throw your desired exception at each step... (if you supply an additional argument where you deliver your custom exception, e.g. mapIfNotNull(T value, Supplier<RuntimeException> exceptionSupplier)). Or simply use Objects.requireNonNull as Holger states in the comment, if NullPointerException suffices your needs.

If you also want to check, whether the object, where you call your getXX on is not null, you may want to use a method as the following:

static <S, T> T map(S source, Function<S, T> mapFunction) {
  if (source == null) { // or again: Objects.requireNonNull(source,..)
    // throw exception due to source
  }
  T value = mapFunction.apply(source);
  if (value == null) { // or Objects.requireNonNull(value,..)
    // throw exception due to missing value
  }
  return value;
}

calling works nearly the same:

A a = ...
B b = map(a, A::getB);
C c = map(b, B::getC);
D d = map(c, C::getD);
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download