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:
- must rebalancePortfolio() really accommodate receiving a null account?
- do some accounts really have a null portfolio?
- should there be else blocks that do something?
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:
- Null Object pattern, which I find to be on the heavyweight side for most scenarios.
- 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.
- 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.
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.
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.