Saying more than nothing

July 23, 2012 »best-practice

As I was reading around on StackOverflow, digging through other peoples source code, I spotted multiple methods returning null in a variety of circumstances. I also found rather imaginative ways to handle null-values.

Although there is nothing wrong with null, the concept of null-pointers seems to be widely misunderstood. Let’s change that.

What are null-pointers?

Wikipedia has the following to say:

A null pointer has a value reserved for indicating that the pointer does not refer to a valid object.

In Java, there are two types of variable-types: value-types and reference-types. The first group includes the primitive data-types like int, float, char and byte. The second contains objects.

Since only reference-types can be null (a reference to nothing), it’s not possible to set a value-type to null (other then in C# where there are “nullable types”). Side-Note: In Java, you can set a value-type to null, by using the appropriate boxing-type (which is a reference-type) and setting it to null.

When trying to invoke a method or access a field on an object with a value of null, a NullPointerException will be thrown.

When to use null

A value of null should only be used when effectively saying nothing. For example, it might be important that there is a difference between an empty message (string ""), and no message at all (as in the BufferedReader.readLine()-method).

A more practical example is given in “C# in Depth” by Jon Skeet, Chapter 4, “Saying nothing with nullable types”:

[…] an example might be an e-commerce application where users are looking at their account history. If an order has been placed but not delivered, there may be a purchase date but no dispatch date […]

In those cases, where there is just nothing to say about the value, you should use null.

The dangers of null

As it should now be clear when to use null, let’s take a look at some (common) anti-patterns which don’t use it appropriately.

Never use null to indicate an error

Quite often, developers tend to return null when something went wrong in the method. Here is a classic example:

// Anti-pattern. DON'T DO THIS!
public Foo readFoo(File file){
    try {
        // Try reading the contents from the given file
        return actualReadData;
    } catch (IOException e){
        e.printStackTrace();
        return null;
    }
}

This code will (given that the reading part is actually implemented) read the contents from the passed file-argument, create a new Foo-object and return it. If however the method fails to read the contents from the file, it will return null.

This pattern is bad for various reasons. At first, it has to be explicitly documented under what circumstances the method returns null. One could argue that, when null is returned, the given file simple contained no data at all.

Using null as an indicator of failure in the method is even worse, because you don’t get any extra information about why the method failed. Also, it might be possible to recover from certain failure cases, but you can’t differ between null and null (which only tells you that something went wrong). Last but not least, returning null does not force any handling of the error-condition. All those drawbacks can be overcome by throwing an exception instead.

Yoda Conditions

As cool as the name sounds, “Yoda Conditions” are a danger because they completely defeat the purpose of making a value null. Here is an example:

String possiblyNull = possiblyReturnsNull();
if ("constant".equals(possiblyNull)){
    // it equals!
} else {
    // it doesn't.
}

Let’s assume that possiblyReturnsNull() does not return null to indicate an error but actually, to return nothing. Using the “Yoda Condition”, no NullPointerException will be thrown. But this also indicates that: null == !("constant") (read: “null is the same as everything which is not “constant”).

It is here not possible to react on a non-existing value, e.g. nothing. Since there should be a reason why possiblyReturnsNull() returns null instead of an actual value, you should test and react on this case. Because if it does not make any difference if the value is null or anything which is not “constant”, it’s probably a design-error.

Never return null for arrays/collections

This is a special case. Let’s say you have a function which returns all Unicorn-toys still in stock:

// Anti-pattern. DON'T DO THIS!
public List<Unicorns> getUnicorns(){
    if (unicornList.size() == 0){
        return null;
    }
    // ...
}

This case is special because we want to say that there are currently no unicorns in stock. So why is this bad?

Because in this case, nothing is equal to 0. Therefor, you can (and should) return an empty array/collection, instead of null:

// Return empty arrays/collections instead of null
public List<Unicorns> getUnicorns(){
    if (unicornList.size() == 0){
        return Collections.emptyList();
    }
    // ...
}

This enables the user of the method to simply iterate over the returned list, without needing to fear a NullPointerException. Because (as mentioned above) in this case, null and zero-length are the same.

You don’t even need to instantiate a new collection, as the Collections-class provides the methods emptySet(), emptyList() and emptyMap() to return an empty, immutable collection for the given type of collection.

As a bonus, you might consider the case in which those methods normally always return an array with at least one element. When the calling code does not check for a null-return value, it might work for years, until the day there are no more unicorns. And then, the big search begins.

Conclusion

To sum it all up:

  • Use null when the value is nothing
  • Don’t return null to indicate an error!
  • Check for null, if it indicates a different result then “not what you’re looking for”
  • Don’t return null for empty arrays/collections

Posted by Lukas Knuth

Comments

comments powered by Disqus