RSS

A Case Against Using Null. (For Almost Anything.)

10 Jan

Java is my usual language, but this goes for everything.

I promise that this is not going to be another rant about NullPointerExceptions and their kin in other languages. This is not to say that such rants are not warranted, and even cheered; but I’m going to be a bit more academic about it. I’m also going to provide solutions, not only the ones available in Java 8, but what I used to do beforehand.

What Does “Null” Actually Mean?

Great question. Null as an adjective, according to the dictionary, means without value, effect, or significance. It also means, lacking, and nonexistent. It also means, empty. And, lastly, also probably most recently, it means zero. This is most likely a linguistic artifact, as everything is ultimately expressed in the binary on a computer. In C, null actually does equate to zero. However, this necessity has led all of us to a lot of abuse, because symbolically it isn’t what null is for. I’ll come back to that.

Null’s etymological origin comes from the latin nullus, meaning none, as in, it-has-not-been-set. While zero is reasonable, zero is an actual number. If you were enumerating the entries of a set of numbers, and you wanted to count the length of that set, you would not skip every entry that was zero, would you? However, you likely would for null, as it denotes no-entry in the set. Therein lies the critical difference.

In Java, objects are initialized to null, before they are set to any value. Object instances are the Java equivalent of C’s pointers; and while they cannot be without a value, they initialize to a language constant that reflects the absence of an intended one. Null is typically represented as zero, but not always, and I am unsure of the case with Java. However, a null pointer is a symbol, it is not reasonably a pointer to the zero position in memory. This position does, actually, exist; but on the reference of such a pointer, the virtual machine (or platform) throws up a red flag.

The nasty habit of using null as a return value when something goes wrong in an operation is almost ubiquitous, but unless this literally represents that no value has been set, it is a dangerous move. I’ve even seen it in the JDK. The response to such an ill-though-out method is usually a few lines of defensive programming, checking to see whether the object is null, and acting accordingly.

Java 8 Solutions

If you aren’t a Java programmer, you may wish to skip this section.

As it turns out, the defensive programming response to returned nulls is so similar, in every instance, that it can be encapsulated into an object itself. This would be Java 8’s Optional. Optional represents a possible value, that is, a value which cannot be guaranteed to exist. However, the Optional itself is never null.

On initialization of an Optional, it is best to set it to Optional.empty(), that is, an Optional with no contents. If a value is being wrapped in an Optional, use Optional.of(). If the presence of the value is unknown, use Optional.ofNullable(), it will do the defensive work for you. The rest of the methods of Optional apply Java 8’s influences from functional programming. What used to require complex if statements is now done primarily through ifPresent(…) and orElse(…).

This might seem like an overreaction to you. However, compared to the work that I used to have to do just to catch every wrench-in-gears value that might pass by, it is a miracle. If you disagree, you need only ask yourself how frequently you have been getting NullPointerExceptions. Adopt Optionals, and you won’t get them anymore.

Older Java Solutions

In previous versions, several further techniques have been added. The biggest problem with “!= null” is that it is an operation, and a mandatory operation, which will slow down code very slightly. This is imperceptible for the vast majority of programs, but if you need something to run searing fast, then it can be unacceptable.

If you are writing an API, I might suggest funnelling all input through a defensive checking method before passing it along to the meat methods; but if you are writing code that only you will access, there is a simpler solution: assertions. This is particularly true for unit testing with programs like JUnit.

This is exclusively functional during development, as in order to enable assertion testing, you need to pass the -ea parameter to the java compiler. Unless you can force this on users, it is exclusively meant to help you identify routes by which null, or any other unacceptable value, can make it to your methods.

The syntax is simple. Given parameter “x”:

assert x != null : "[error message]";

If the provided boolean expression evaluates to false, an AssertionError is thrown, with a message of the toString() value of whatever was passed on the right (for me, most typically an actual string).

I don’t generally like to see assertions making their way into production code today, as I am inclined toward Optionals; but this is quite effective for debugging. Additionally, such statements can be considered an essential part of JUnit tests. If you are in a rush, it is possible to ignore all assertions remaining in a slice of code by removing the “-ea” parameter from the compiler; but on the human end, this is bad practice and worth avoiding.

As an alternative, Apache Spring has a class called Assert which handles more or less the same tasks as the assert keyword.

Broader Solutions for Object Oriented Languages

At last, in the most general sense, there is the Null Object Pattern. This is, still, my ultimate preference when building a set of classes, as there is no need for Optional when null never enters the equation.

A Nullary Object is an object extending the appropriate interface, with defined behavior, denoted as equivalent to null. This has its ups, and its downs. As an example, suppose we had this interface:

public interface Animal {
    public String speak();
}

with these implementations:

public class Dog implements Animal {
    public String speak() { return "bark"; }
}

public class Cat implements Animal {
    public String speak() { return "meow"; }
}

public class Bird implements Animal {
    public String speak() { return "tweet"; }
}

And we had one further class that requires one unknown animal, which will indubitably call “speak()”. Which animal is beyond our control, and we don’t want our program to crash on a NullPointerException simply because no animal was specified. The solution is one further class:

public class NullaryAnimal implements Animal {
    public String speak() { return "…"; }
}

In the case of abstract classes, it is often helpful to have the nullary class be a member of the class itself. This is also particularly helpful when there are multiple behaviors which might, otherwise, be implemented as “null”. The potential down side is for people who were actually looking for an exception to be thrown; in such a case, simply fill speak() with an Apache Commons NotImplementedException or something relatable.

One extension of this pattern is such:

public abstract class Sequence {
    //...
    
    public static final Sequence ANY = new Sequence(…);
    public static final Sequence ALL = new Sequence(…);
    public static final Sequence NONE = new Sequence(…);
}

In this instance, a new Sequence can be initialized to Sequence.NONE, ALL, or ANY, and be replaced if a new value is provided. Additionally, since these are actual objects and constant values, they respond appropriately to equals checks.

There may be a name for this pattern, I’m honestly not sure. I came up with it on my own, but I very much doubt that I’m the first.

Conclusion?

Hardly. However, you now hopefully have a new set of tools to keep unfinished declarations and, even worse, “= null” statements out of your program. I hope I’ve made your life easier!

Advertisements
 
Leave a comment

Posted by on January 10, 2015 in Programming

 

Tags: , , , , , , , , , , , , , , , ,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: