By Reinier Zwitserloot

null.

It’s one of the first things you learn in Java; if not because your teacher or tutorial is outright telling you all about it, you’ll be confronted with a NullPointerException.

Let’s revisit the topic. I’ll show you a few tricks to sidestep the need to deal with null, or null lookalikes, entirely.

getOrDefault

null shows up in many places in Java, but one of the most common ones is as a return value to signal ‘value not found’, ‘value not initialized yet’, etcetera. For example, java.util.Map has a get() method that returns null to signal that the provided key isn’t in the map.

Let’s think about that for a moment: What ought to happen if you do a key lookup on a Map but that key isn’t in the map? Well, that depends on your code of course, but three answers cover just about all the use cases:

  • This really shouldn’t happen; a bug has occurred. The most appropriate thing to do here is to ‘crash’ with a stack trace that should allow a developer to fix the bug quickly. Clearly a NullPointerException is the desired outcome for this scenario.
  • The code should continue and assume some default value. In essence, the map lookup should be followed up with something along the lines of: if (value == null) value = "Default";
  • The code should ‘branch’; if the key is not found, a different snippet of code should run. An if is appropriate here.

The API of Map‘s get() method caters to the first and the last case easily enough: if (value == null) is easily written, and just dereferencing the returned value will throw a NullPointerException without any effort required from the programmer. However, that second case is quite common.

Java8 to the rescue! With the arrival of default methods, a number of core APIs have received some nice upgrades, and Map hasn’t been left out. Starting with java 8, Map has the getOrDefault method. You used to have to write:

String value = map.get(key);
if (value == null) {
    value = "DefaultValue";
}

But now you can write:

String value = map.getOrDefault(key, "DefaultValue");

Either The value variable holds a value from the map, or it holds the default value; in either case, value is not going to be null here.

If you are maintaining or writing API yourself, make sure to add .getOrDefault style methods! You can even take a page from java.util.Maps playbook and use java8’s new default methods in interfaces feature to add them to your APIs right now.

Tip #1: Use .getOrDefault() where available, and add these to your own APIs.

’empty’ instances

Search github, or even your own codebase, and you’ll probably find lots of code that looks like this:

String x = ....;
if (x == null || x.isEmpty()) // do something

This is indicative of bad null handling. Whenever you write a method that returns null, consider if there is some instance of the type you are supposed to return that has the same semantic meaning. For example, there are many methods where there is no useful difference between returning null and the empty string, or null and an empty list.

If you write such APIs, you should never return null — return an appropriate empty/blank instance instead. If your return type has a globally cached immutable ’empty’ object (such as java.lang.String‘s "", or java.util.Collections.emptyList()), that’s a sign that you should consider just returning that instead of null.

There are some cases where there is a difference between the notion of ‘I have a result for you, and it is the empty object’ and ‘I have no result for you’. In such cases, null is still useful, but conversely the result won’t be involved in an if (x == null || x.isEmpty()) style check either, so the usage of null won’t feel like inelegant API.

Tip #2: Consider returning empty lists and strings instead of null.

Enums

You might be tempted to treat null itself as simply another possible result. For example, a method that returns the state of a 3-way switch (north, south, or center), returning Boolean.TRUE for north, Boolean.FALSE for south, and null for center.

Please don’t do this!

Callers of such an API can’t chain your method (they cannot immediately dereference the result of calling your getter), and ‘Boolean’ isn’t very descriptive. In fact, it is quite the opposite: It strongly suggests 2 values, not 3.

Java has had enums since version 1.5 – it’s time to adapt. Such methods should return an enum. You’d create something like this:

public enum SwitchStatus {
    NORTH, SOUTH, CENTER;
}

and return one of these three values (and, consequently, never null).

Tip #3: Use enums to represent the return type of methods that return one of a limited set of results.

Fail early

If you write a method that is not designed to accept null for one or more of its parameters, you should add a null-check that throws a NullPointerException if one of the arguments is null. By doing this, you ensure that all potential bugs are reported so that the developer knows exactly what went wrong, and where to look in the source code. After all, they will get a stacktrace that pinpoints both the problem and the location of the problem. The message of the exception should be the name of the parameter that was null.

Aside from producing a stack trace that enables a quick bug fix, failing early has another advantage: Your code won’t fail halfway through the operation, potentially stuck in an inconsistent state.

You can use Project Lombok’s @NonNull feature to automatically inject these null checks into your class files without having to look at them in your editor. Check out the @NonNull feature page for examples and more information.

Tip #4: Check for invalid nulls in parameters supplied to your API, and throw NullPointerException upon detecting them.

Use your IDEs!

A major issue with null that we haven’t yet dealt with is the fact that null in java is not type checked: If a method is declared to return a java.lang.String, you have no idea if the method might return null, or if it never will.

Fortunately, we actually have a way to mark our methods with whether or not they can return null and it is even completely backwards compatible! We have annotations to do this.

Unfortunately, there isn’t an industry standard (yet?). The major IDEs ship their own versions of these annotations, and the JSR305 annotations are also popular. JSR305 looks like this:

@javax.annotation.Nonnull public String methodThatNeverReturnsNull() { ... }

@javax.annotation.Nullable public String methodThatMightReturnNull() { ... }

Eclipse has its own annotations, named @NonNull, @Nullable, and @NonNullByDefault.

IntelliJ IDEA also has their its annotations, named @NotNull and @Nullable.

Both IDEs work ‘out of the box’ with their own annotations (for eclipse, you need to turn on the setting Enable annotation-based null analysis which you can find in Java > Compiler > Errors/Warnings > Null Analysis and can be configured per project or globally for the entire workspace). Both IDEs can also can be configured to treat JSR305‘s annotations intelligently.

Once you have configured your IDE and added these annotations to your own code, your IDE will generate warnings when you forget to null-check a return value from a method that has been declared with @Nullable, and similarly, if you null-check a return value from @NonNull method, you get a warning. These annotations also show up in the documentation of your API.

Tip #5: Configure your IDE today and start using annotations instead.

Do not use Optional!

Java 8 has introduced lambdas, the stream API, and default methods. But, Java 8 also brings java.util.Optional which echoes the name of an alternate no-value approach commonly used in languages like Scala. The idea of Optional is simple: instead of returning a String that may or may not be null, you return an Optional<String>, which will never be null. It has methods that you can use to figure out if this optional represents a value, or if it represent no value.

Optional seems to have it all: It avoids null and, because it is a type, returning an Optional ensures that callers are aware of the fact that you might return a no-value result.

However, don’t be fooled; Optional is not a great idea. Any of the tips provided in this article are a much nicer solution; after all, Optional is just null restated with another name, except, less flexible:

  • null is used in java to represent uninitialized fields. You can’t use Optional for this, unless you rewrite all your fields to be of type Optional<X> instead of just X. This is problematic: You now have to deal with generics erasure, Optional is notserializable, and optional is a wrapper object; short lived wrappers are cheaply garbage collected, but long-lived ones cause performance problems.
  • It is not possible to write a method that takes either an X or an Optional<X>. Callers of your method are forced to unpack the optional immediately. Java has no pattern matching construct (unlike most languages that have had Optional since the beginning – for example, Scala), which means unpacking optionals is less legible than just null checking the result.
  • Any pre-existing API that uses null to signal no-value cannot be changed to return an Optional instead. For example, java.util.Map has a get() method, and if anything ought to return an Optional<V>, surely that should! Unfortunately, it doesn’t and it can’t be made to do so, because Java does not make backwards-compatibility breaking changes (if it did, Enumeration and Vector would have been eliminated years ago!)

Also consider the words of Brian Goetz, the Oracle engineer who introduced Optional:

You should almost never use it as a field of something or a method parameter. I think routinely using it as a return value for getters would definitely be over-use.

This brings us to our final tip:

Tip #6: Don’t use optional! *

* If you are writing java 8 stream API methods, Optional might possibly be a good idea, but that’s about the only place you should consider it.

We’ve been to null and back!

Let’s review:

  • Learn about additions to the core java libraries, such as Map’s getOrDefault(), and use similar constructs when writing your own.
  • Consider returning empty collections, empty strings, and other empty instances instead of null.
  • Use enums where appropriate.
  • Check your method inputs and fail early if null is unexpectedly passed as argument.
  • Add annototation-based null checking to your IDE and start adding @NonNull and @Nullable to your methods.
  • Forget about Optional.

With these six tips in our pocket, we can learn to embrace null as a fine part of the Java language, generating great stack traces for us, representing lack-of-value in a way that is easily checked, which doesn’t break our type hierarchies, or our APIs.

Embracing the Void: 6 Refined Tricks for Dealing with Nulls in Java

| Java Language| 7,774 views | 7 Comments
About The Author
-

7 Comments

  • mariofusco
    Reply

    1. “null is used in java to represent uninitialized fields”

    I thought this is what constructors are for, isn’t it? So either you can initialize a field in the constructor or it has a meaning in your domain to leave that field empty and in this 2nd case it should be represented with an Optional.

    2. “It is not possible to write a method that takes either an X or an Optional”

    These methods clearly have 2 different semantics and then it is correct that they are 2 totally separated method. In the first case you pass a value of type X that for sure is there, in the second a value of type X that could be also possibly empty. The fact that you have to distinguish these cases forces you to correctly reason on them.

    3. “Callers of your method are forced to unpack the optional immediately”

    You have no idea of how the map and flatMap methods work, right? 😉

    4. “Any pre-existing API that uses null to signal no-value”

    “Absence of a signal should never be used as a signal” – J. Bigelow, 1947

    5. “For example, java.util.Map has a get() method, and if anything ought to return an Optional, surely that should! Unfortunately, it doesn’t and it can’t be made to do so”

    Optional.ofNullable(map.get(key));

    it isn’t that difficult in the end 😉

  • arnaud
    Reply

    I don’t fully agree about Optional (even if @Nullable annotation is useful).
    It makes null handling more robust and composeable, for example:

    interface Address {
    Optional getCountry();
    }

    interface User {
    Optional getAddress();
    }

    System.out.println(“address: ” + user.getAddress().flatMap(a -> a.getCountry()).orElse(“N/A”));

    Before Java 8 and lambda, it was too verbose (e.g. Guava Optional in Java 7) but now it’s more or less like Scala Option[T].

  • yves
    Reply

    Good stuff. Agree with the list of tips with one exception: Tip #6: Don’t use optional!

    That tip seems a bit too absolute. Java’s Optional has its uses (although limited). And therefore it would be more informative to explain its valid use cases instead of completely banning it. For example, Repository methods returning either a single DB record (entity) or the the absence of one.

    Still Brian Goetz’s answer on Stackoverflow is very good.

    Finally, instead of “we can learn to embrace null as a fine part of the Java language” I would recommend a highly null-unfriendly programming style as can be achieved by applying your tips 😉 (Certainly never ever embracing null as something good).

  • Richard
    Reply

    w00t. I’m glad to see people calling out Optional. In cases where Optional is viable a collection is almost always a better choice. There are strong parallels between Optional and the now derided Singleton pattern, we would be wise to heed them.

  • Some penguin
    Reply

    I’ve worked in some life-critical software in france.
    backing ‘yves’ :

    1. Just use defensive coding, everywhere
    Example: if(null != variable) // do useful thing with variable
    Yes, NPE may still happen, but in that case its a *real* one :
    s/o run a debugger and deliberately introduced a null
    2. Dont try to solve ‘comfort’ issues with technology
    Optional is certainly great, but not a replacement for defensive coding.

    To not be only negative or stupidly sarcastic, let’s get to the point
    A. You can’t prevent totally nulls – think about putting a conditional breakpoint where the condition is { myhackedvar=null; return false; }
    B. You can’t just “not bother”
    C. So apply the ‘best practices’ except when performance is a concern – already seen those grafs where !=null is the most consuming task of your software ? Then begin by checking if(“constant”.equals(var)) instead of if(var.equals(“constant”))

    Finally when performance is a concern, and I understand this is a personal point of view :
    If you really need behaviour for null,
    Even after applyfin defensive coding,
    *Then* it’s time for a different paradigm – such as AFAIC when you decide to switch from BigDecimal to CustomMoneyClass to handle all OverflowedValue, NaNValue and ApproximatedDoubleValue : after your logic is perfect, your lack IS semantic. 😉

    This may look like a very picky opiniated solution but
    1. I back up this article analysis
    2. Triple checking is *one* solution
    3. My optimistic experience feedback is – if you really did your job and NPE workarounds are not comfort-patterns,
    Then it’s a good and fast way to spot real *business* missed feature

    2 cents.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>