Close

Java - Avoiding possible NullPointerException with method call chain

[Updated: May 12, 2020, Created: May 11, 2020]

Java 

Following examples shows how to avoid NullPointerException within chained method calls.

Examples

Example POJOs

package com.logicbig.example;

public class Person {
  private Address address;
    .............
  static Person getPerson(){
      Person person = new Person();
      Address address = new Address();
      person.setAddress(address);
      return person;
  }
}
package com.logicbig.example;

public class Address {
  private City city;
    .............
}
package com.logicbig.example;

public class City {
  private String name;
    .............
}

Calling chained methods

package com.logicbig.example;

public class Example {

  public static void main(String[] args) {
      String name = Person.getPerson().getAddress().getCity().getName();
      System.out.println(name);
  }
}
Exception in thread "main" java.lang.NullPointerException
at com.logicbig.example.Example.main(Example.java:6)

Fixing with if bocks

package com.logicbig.example;

public class Example2 {

  public static void main(String[] args) {
      String cityName = null;
      Person person = Person.getPerson();
      if (person != null) {
          Address address = person.getAddress();
          if (address != null) {
              City city = address.getCity();
              if (city != null) {
                  cityName = city.getName();
              }
          }
      }
      if (cityName == null) {
          cityName = "N/A";
      }

      System.out.println(cityName);
  }
}
N/A

By using java.util.Optional

package com.logicbig.example;

import java.util.Optional;

public class Example3 {

  public static void main(String[] args) {
      String name = Optional.of(Person.getPerson())
                            .map(Person::getAddress)
                            .map(Address::getCity)
                            .map(City::getName)
                            .orElse("N/A");
      System.out.println(name);
  }
}
N/A

Creating reusable util methods with Optional

We can create util methods which can use java.util.Optional API internally:

package com.logicbig.example;

import java.util.Optional;
import java.util.function.Function;

public class NullSafeUtil {
  public static <R, T, A> R eval(R defaultValue, T input,
                                 Function<T, R> function) {
      return Optional.ofNullable(input)
                     .map(function)
                     .orElse(defaultValue);

  }

  public static <R, T, A> R eval(R defaultValue, T input,
                                 Function<T, A> function,
                                 Function<A, R> function2) {
      return Optional.ofNullable(input)
                     .map(function)
                     .map(function2)
                     .orElse(defaultValue);
  }

  public static <R, T, A, B> R eval(R defaultValue, T input,
                                    Function<T, A> function,
                                    Function<A, B> function2,
                                    Function<B, R> function3) {
      return Optional.ofNullable(input)
                     .map(function)
                     .map(function2)
                     .map(function3)
                     .orElse(defaultValue);
  }

  public static <R, T, A, B, C> R eval(R defaultValue, T input,
                                       Function<T, A> function,
                                       Function<A, B> function2,
                                       Function<B, C> function3,
                                       Function<C, R> function4) {
      return Optional.ofNullable(input)
                     .map(function)
                     .map(function2)
                     .map(function3)
                     .map(function4)
                     .orElse(defaultValue);
  }
  //more overloaded methods
}
package com.logicbig.example;

public class Example4 {
  public static void main(String[] args) {
      String cityName = NullSafeUtil.eval("N/A", Person.getPerson(),
              Person::getAddress, Address::getCity, City::getName);
      System.out.println(cityName);

      City city = NullSafeUtil.eval(null, Person.getPerson(),
              Person::getAddress, Address::getCity);
      System.out.println(city);

      Address address = NullSafeUtil.eval(null, Person.getPerson(),
              Person::getAddress);
      System.out.println(address);
  }
}
N/A
null
Address{city=null}

By catching NullPointerException

package com.logicbig.example;

import java.util.function.Supplier;

public class NullSafeUtil2 {
  public static <R> R eval(Supplier<R> chainSupplier, R defaultValue){
      try {
          return chainSupplier.get();
      } catch (Exception e) {
          return defaultValue;
      }
  }
}
package com.logicbig.example;

public class Example5 {

  public static void main(String[] args) {
      String cityName = NullSafeUtil2.eval(() -> Person.getPerson().getAddress().getCity().getName(), "N/A");
      System.out.println(cityName);

      City city = NullSafeUtil2.eval(() -> Person.getPerson().getAddress().getCity(), null);
      System.out.println(city);

      Address address = NullSafeUtil2.eval(() -> Person.getPerson().getAddress(), null);
      System.out.println(address);
  }
}
N/A
null
Address{city=null}

Note that, allowing runtime exception is considered to be relatively expensive process than to avoid it. It is due to the fact that throwing exceptions require various stacktrace objects to be initialization.

See Also