Nuke the NPE in Java

March 19, 2014

Null is a pain and it is time to nuke the NPE (NullPointerException) in Java!

Actually, NPEs are trivial to fix, so they’re not actually what this blog post is about.

We’ve all seen code like above throw a NPE, and we know the fix is trivial:

It is safe code now, that indeed eliminates the NPE, but it yields uncertainties:
  1. must rebalancePortfolio() really accommodate receiving a null account?
  2. do some accounts really have a null portfolio?
  3. should there be else blocks that do something?
Question (1) is easily addressed: throw a NPE if account is null. This is reasonable behavior – put the onus where it belongs, on calling code to handle null accounts appropriately. The answer to question (3) in this particular example is probably no. Question (2) remains an uncertainty, and guess what – we also need to iterate over holdings:
If you really wanted safe code, I guess this is the style of code you would write.  …and then another developer would come along, see it, and mimic it. In time, null check if statements would dominate your codebase. Putting an end to this craziness is what this blog post is about.

Eliminate Null

OK, null references in Java cannot really be eliminated. However, adhering to one principle can reduce their effect and put an end to most of the craziness demonstrated above:

Do not pass null references around in code.

Obviously, “unset” is a valid and useful state for an object reference. Fortunately, facilities other than null references have been created to represent null objects:

  1. Null Object pattern, which I find to be on the heavyweight side for most scenarios.
  2. Optional<T> within Google’s guava-libraries. At Wealthfront we use Optional<T> extensively, although we use our own incarnation – see A Better Option for Java blog post.
Consider a simple Person class:
Several subtleties about this class are worth calling out:
  • For simplicity’s sake, all strings (including empty string, whitespace strings, etc.) are being accepted as valid names.
  • The constructor receives firstName and lastName as String references, checks them for null, and stores them within final class fields. This makes it trivial to see that getFirstName() and getLastName() never return null. Furthermore, checking for null in the constructor creates a fail fast class which throws a NPE with a meaningful stack trace.
  • The constructor receives  middleName as an optional String and stores it as one without checking the Optional<String> reference for null. Never ever set Optional<T> references to null, and don’t check them for null since doing so would raise suspicion that maybe they are sometimes null.
  • getChildren() returns an empty list – not null!
  • Callers of getFirstName(), getMiddleName(), getLastName(), and getChildren() should not check the returned value for null. Doing so would raise suspicion that maybe these getters do return null in some cases.

Reality Check

Code in the real world passes around null: libraries return null, JSON contains null, null is stored in the database, etc. Eliminating null is unrealistic, so a reasonable goal is to limit its propagation.

For example, at Wealthfront we use the TwoLattes JsonMarshaller and we use Hibernate. Neither library supports the Person class as written above, primarily due to the missing default constructor and due to the Optional<String> field (although the latter could be overcome through registration of a custom type handler).

Minor edits to the Person class produce an entity class supported by TwoLattes JsonMarshaller and by Hibernate. It is shown here without its database id and without its Hibernate mappings. An exposed default constructor and non-final fields set through reflection now open the door for null to creep in, but its public API still clarifies intention, implying that null firstName / lastName values are unexpected error cases.

Tips

In closing, here are a few suggestions related to null in Java:

  • Avoid adding null to collections. Docs for Google’s guava-libraries contain some discussion here.
  • Avoid null enum values. If needed and if acceptable, add an UNKNOWN enum value (or similar).
  • Use String.valueOf(obj) instead of obj.toString().
  • Use “apple”.equals(myString) instead of myString.equals(“apple”).
  • Prefer value types like long over reference types like Long. If Long is required, avoid setting it to null – unboxing will throw a NPE!
  • Mark variables final when possible, and initialize them to non-null objects.
  • java.util.Objects.requireNonNull() is a nice one-liner built into the Java API for throwing a NPE (with optional message) if the provided reference is null.
  • Tools like FindBugs and IntelliJ support various null related annotations as expressions of developer intent. Such annotations are used to issue warnings more appropriately during their code analysis. JSR 305 seeks to standardize such annotations.