Monthly Archives: September 2015

null

The Problem

References to objects may be empty, i.e. they may not reference a particular object at all. In this case, their value is null. Whenever you want to access an object using such a reference, you have to make sure, that the reference is not null. Otherwise Java punishes every access to null with a NullPointerException.

There are different ways to take care of the null case in Java. But we start a discussion, we first need a small example to talk about: The method getUserData(int userId): UserData fetches the data of the user with the given userId from a data store. In case there is no user data for the given userId, it returns null.

In principle, you have two alternatives: either handle the null value, or avoid it. Sometimes you can choose yourself, other times somebody else already decided for you (e.g. when you use 3rd party code that returns null values from methods).

Handling null

Check or Catch

The first step to handle a null value is to either guard it with a check for null, or to catch the NullPointerException that will be thrown when accessing the null value.

// without a guard, prone to throw NPEs
if ("Hans".equals(getUserData(currentUserId).getName())) {
    //...
}

// using a null check as guard
UserData userData = getUserData(currentUserId);
if (userData != null)
    if ("Hans".equals(userData.getName())) {
        //...
    } 
} else {
    // handle the case that there is no user data for currentUserId
}

// catching the NPE
try {
    if ("Hans".equals(getUserData(currentUserId).getName())) {
        //...
    }
} catch (NullPointerException e) {
    // it is not that obvious, but only getUserData(...) could lead to a NullPointerException here
    // handle the case that there is no user data for currentUserId
}

Which one you choose depends on the situation. Using the first approach makes it easier to make explicit what the null value means. Imagine you have to make several calls that can return null in the same expression. In this case it is not clear which one threw the exception that you are handling:

// catching the NPEs of multiple calls
try {
    if ("Hans".equals(getUserData(userId1).getName()) || "Hans".equals(getUserData(userId2).getName())) {
        //...
    }
} catch (NullPointerException e) {
    // which user ID could not be found?
}

Null Annotations + Static Analysis

Finding all locations where you need to handle a null value is tedious, and doing it by hand, you will never be sure if you found all locations. Fortunately there are tools that support you. All current tools for static code analysis provide a possibility to check for unhandled null values. But those tools need a little bit of help, since they cannot fully automatically infer the coder’s intention (may a value be null or not). Usually the tools use some kind of annotation mechanism that you can use to give a hint about your intentions.

Unfortunately there is no standard for this annotations. There even exist two JSRs that are related to this topic. JSR-305 originates in the FindBugs community. It tries to standardize the annotations that FindBugs provides since years. JSR-308 describes a more generic extension of the Java annotations itself. Eclipse provides annotations based on this JSR that allow a null analysis within the IDE, done by the Eclipse compiler. There is a good overview on Stackoverflow about the current state of affairs regarding null annotations in Java.

In the end, this all means that it depends on your tool chain, which annotations you can use.

Avoiding null

Throw a Meaningful Exception Instead of Returning Null

Instead of returning null, the method could also throw a more meaningful exception. This would at least make the purpose of the catch block more obvious.

// catching a meaningful exception
try {
    if ("Hans".equals(getUserData(currentUserId).getName())) {
        //...
    }
} catch (UserNotFoundException e) {
    // handle the case that there is no user data for currentUserId
}

It does not solve the problem of knowing which of multiple calls in the same expression threw the exception. Therefor the exception should provide additional information to identify the cause.

// catching a meaningful exception
try {
    if ("Hans".equals(getUserData(userId1).getName()) || "Hans".equals(getUserData(userId2).getName())) {
        //...
    }
} catch (UserNotFoundException e) {
    String missingUserId = e.getUserId(); // <-- this user id was not found
    // handle the case that there is no user data missingUserId
}

Null Object

A null object is an immutable instance of a class or interface that behaves in a natural and harmless way. This means, you can use this object like other instances, but your interaction with it will have no effect. In our example, the null object might return the empty string as name:

public static final UserData NULL = new UserData() {
    public String getName() {
        return "";
    }
};

Using this, our example would look like the following:

UserData userData = getUserData(currentUserId);
if ("Hans".equals(userData.getName())) {
    //...
} 

We can use the userData object safely here without a check for null. Only if we want to handle the case that there is no userData available for the currentUserId, we need to check if userData == UserData.NULL.

Usually you have only one static instance that is used as NULL object. This way you can easily identify the null object, if you need to. As a variation, you can have several different static instances, if there are different notions of ‘natural behavior’ (in our example it might make sense to have a SUPERUSER object, for example).

Option

The Option Type construct combines the idea behind Null Object with polymorphism and generics. It provides a container for a given type T, that may or may not contain an instance of T. You could ask the container if it contains an instance, and you can ask the container for the instance it contains. This construct is available in many languages in one or the other way. Many of those languages provide two different subclasses for the case that there is something and for the case that there is nothing.

Option Type

Since Java 1.8, the Java runtime library provides the type java.util.Optional, without the usual specialized subclasses. In our example this would look like the following:

Optional<UserData> userData = getUserData(userId);
if (userData.isPresent()) {
    if ("Hans".equals(userData.get().getName())) {
        //...
    }
} else {
    // handle the case that there is no user data for currentUserId
}

In comparison to all other variants, here the code itself makes it pretty obvious that user data could not be available and we are handling this case also. Using Optional as a return type also ensures that you do not miss a case accidentally. If you don’t handle the null case or do not even check with isPresent(), then you do this by intent.

Conclusion

There are so many ways to prevent a NullPointerException. Please use them!