A Brief History Of Nothing

The famous computer scientist Sir Tony Hoare is often credited for the invention of null, and by extension the bane of Java software developers, the NullPointerException (NPE). He calls it his “Billion Dollar Mistake.”

But is null really a mistake? NPEs are really just a case of a variable in an unusable state, and that’s not Tony Hoare’s fault. Before NPE, those with long memories will recall an error called a S0C7 (pronounced “sock seven”) that plagued programmers on early IBM platforms just as much. Other languages call null by different names, nil, NaN. But they represent the same root value – nothing.

That said, Tony Hoare is correct that null has become a language element that many programmers feel is more “bug” than “feature”. In this post, we’ll examine why that is, and look at some of the ways languages have evolved to address it.

The Problem of Unusable State

Let’s imagine a simple customer database. And let’s say your database saves a customer’s name, address, and credit card number. But what do you do for customers who decline to have their credit card information saved? What do you do for customers who pay cash in a brick and mortar outlet? Will you refuse their business because your database has a credit card field which must be populated?

Of course not. There is, and always has been, a need to represent a lack of data. You could, for example, populate your credit card field with some obviously un-credit card like value, say all zeroes or all nines. But that is just null by another name. You’d still need to code for the zero or all nine special case. True, you may not experience a crash. But if you aren’t careful, your special value could instead flow downstream and cause problems later on.

The choice of a database as this example is not an accident. Null has been part of SQL design since the very beginning. The creator of the relational database, E.F Cobb made support for null one of his rules for defining what a relational database is. He felt there are just times when a data value may not not available.

So maybe null – or something like null – is unavoidable. However, adding explicit null checks to every possible nullable object adds size and complexity to your code base. Is there anything we can do to help contain and minimize null values?

The Null Object Pattern

One way is to use the Null Object pattern.

In this pattern, a special subclass (or interface implementation) exists to represent the null case, but which still implements the parent class’ interface. The trick is that all of its methods will be no-op (i.e. they do nothing, that is, “no operation”).

You can find a brief discussion of it in “Uncle Bob” Martin’s book, Agile Software Development: Principles, Patterns and Practices. He recommends creating a static variable in each potentially nullable class that implements, as he calls it, “nothing” – although his definition of “nothing” isn’t completely pure. If a method returns a value, for example, then you have to implement something.

In his book, Refactoring To Patterns, Joshua Kerievsky recommends the Null Object pattern, but also points out that it is probably better to use an interface, rather than subclassing. Otherwise, it can create a maintenance risk. When adding functionality to a class with a null sub-class, you’d need to make certain that the null version properly implements (or doesn’t implement) the new interface elements or risk having the base class methods unintentionally executed by your null version.

The “Special Case Pattern”

In his book, Refactoring (and in Patterns of Enterprise Architecture), Martin Fowler suggested a variation on the Null Object Pattern. He calls it the “Special Case Pattern.” In this case, he suggests the creation of Null Objects which, instead of having all no-op methods, implement a meaningful default behavior. For instance, a database of utility customers might return a customer name value of “Occupant” for an address whose owner has not yet been identified. However, as Fowler points out, this pattern requires that a suitable default behavior exists.

Some languages try to provide null-safety syntactically. The null safe operator, for instance, represented by a question mark in languages like Kotlin, allows for the referencing of methods on null objects without throwing errors. For instance:

// SomeValue will either have the value returned by someMethod if
// somethingNullable is not null, or someValue itself will be null
// if somethingNullable is null, propagating the null value.
val someValue: String? = somethingNullable?.someMethod()

Typically, instead of throwing an NPE, this syntax would return null if somethingNullable is null. It would act as if somethingNullable was a null object with a no-op implementation of someMethod.

The“?:” or “elvis” operator in kotlin is another example. For instance:

val someValue: String = somethingNullable?.someMethod()?: appropriateDefaultValue

would evaluate to an appropriate value of the correct type if somethingNullable is null.

Java Optionals

A more comprehensive approach to dealing with the “no value” condition is to use the Option Pattern, also called Optional or Maybe. An Optional is a container for a value that may or may not be present. It can been seen as a collection having one or zero elements.

It’s not perfect and doesn’t prevent all NPE’s. However, the intention is to encourage you to account for nulls in a safe way. You have to go through the Optional in order to get to the nullable object inside. It includes methods which help you deal with cases where a value is or isn’t present. Classes for implementing the Option Pattern are available in languages like Scala and an Optional class was introduced as part of the standard library in Java 8.

Optionals in Java 8

To understand Optionals better, let’s look at some examples of how Optionals work in Java 8. To start, let’s imagine we have a simple Person class with a name, age, and occupation. As coded, any of the variables in this class can be null and an object of type Person can, itself, be null. So this offers us lots of opportunities to explore null safety.


public class Person {
     String name;
     Integer age;
     String occupation;
     Person (String name, Integer age, String occupation) {
          this.name = name;
          this.age = age;
          this.occupation = occupation;
     }
     public void setAge (Integer age) {
          this.age = age;
     }
     public String getName () {
          return name;
     }
     public Integer getAge () {
          return age;
     }
     public String getOccupation () {
          return occupation;
     }
}

Now let’s look at Optional object creation to see what variations are available. In Java, an Optional is a class that wraps a value which may or may not be null – and then offers methods for interacting with the contents in a safe way. And you have to go through the Optional to access the object inside. Think of it as a collection of at most one element in the form:

Optional <Person>

There are a number of ways to create an Optional in Java 8.

Optional.empty()
Optional.of()
Optional.ofNullable()

Let’s start with the first. To create an empty Optional for our Person class i.e. one which contains nothing (analogous to a null object), use Optional.empty().

Optional <Person> optionalPerson = Optional.empty();

From here, we can query our Person Optional to determine the state of the wrapped object. So, for instance, you have the boolean isPresent() method which will return false if the object is “empty” as in the example above:
optionalPerson.isPresent() == false
.

To populate the object inside, you have a choice between two methods, depending on whether or not you know the object isn’t null. If you’re sure it definitely isn’t null, use Optional.of as follows:

Person bob = new Person("Bob", 30, "Computer Programmer");
Optional <Person> person = Optional.of(bob);

If you try to pass null instead of a valid Person, it will immediately throw an NPE. If you have an object that may or may not be null, use `Optional.ofNullable` as follows:

Person bob = null;
Optional <Person> optionalPerson = Optional.ofNullable(bob);

In this case, the value of person will be &quotempty&quot since bob is null. It would be the same as writing:

optionalPerson = Optional.empty();

And now you have all of the null-safe features of the Optional at your disposal. So, for instance, under normal circumstances, you might want to check for null before printing like this:

if (bob != null) {
     System.out.println(bob.getName());
}

But using a Java 8 Optional, the same code can be written as:

OptionalPerson.ifPresent(p -> {
     System.out.println(p.getName());
});

The ifPresent() method works in a similar way to the null safe operator. But instead of conditionally invoking a method, it takes a Consumer function as an argument that gets invoked only if the Optional is not empty. So in this case, the person’s name will only get printed if the Person object inside the Optional is not null. Java 8 also offers variations on the elvis operator, the orElse() or orElseGet() methods. The difference between the two is subtle. The orElse() method expects a default value as an argument while orElseGet() expects a Supplier function that returns a default value. In the example below, resultString will be assigned &quotDefault String&quot if optionalString contains a null object.

String nullString = null;
Optional <String> optionalString = Optional.ofNullable(nullString);
String resultString = optionalString.orElse("Default String");

The orElseGet() version would look like this:

String nullString = null;
Optional <String> optionalString = Optional.ofNullable(nullString);
String resultString = optionalString.orElseGet(() -> {
     return "Default String"
});

A variation on the orElse theme is orElseThrow(). Instead of returning a default value, it would throw an appropriate exception when the object inside the Optional is null. So the example above might look like this:

String resultString = Optional.ofNullable(nullString).orElseThrow(() ->
     new IllegalArgumentException("String is null"));

Lastly, Java 8 Optionals offer another way to access the wrapped object. The get() method will either return the object or throw a NoSuchElementException error if it is empty. So you’d need to first check that the object isPresent() before calling get() – which is little better than explicitly checking for null. So better to stick with the other methods.

You can use filtering to perform inline tests on an Optional. It’s similar to the way a filter acts on a stream – except imagine it is a stream of just one (or none). The filter takes a predicate as an argument and always returns an Optional. If the object wrapped by the Optional is not empty and passes the predicate test, then it is returned as is. But if it is either empty or fails to pass the test, then it returns an empty Optional. So, for instance, if you wanted to determine if a Person wrapped by an Optional was of a certain age, you could write:

public static boolean canVote (Optional optionalPerson) {
     return optionalPerson
          .filter(p -> p.getAge() >= 18)
          .isPresent();
}

We can also perform transformation operations on Optionals using map. The map applies a function to the object wrapped inside an Optional and then returns the result wrapped in an Optional. So, for instance, we can combine a map with a filter to determine if a Person is a computer programmer.

public boolean isComputerProgrammer (Optional <Person> optionalPerson) {
     return optionalPerson
          .map(person -> person.getOccupation())
          .filter(occupation -> occupation.equals("Computer Programmer"))
          .isPresent();
}

But wait. What if our Person is unemployed and has no occupation? In other words, what if the occupation is null? To cover that case, you might want the occupation getter method to return an Optional like this:

public Optional<String> getOccupation () {
     return Optional.ofNullable(occupation);
}

But now our mapping and filtering won’t work. Map will want to return a nested Optional with an Optional <String> inside! So instead of map, we’ll use flatmap. This will return the simple Optional we are looking for. So if we were to write the occupation getter as above, returning an Optional, our computer programmer method would look like this:

public boolean isComputerProgrammer (Optional <Person> optionalPerson) {
     return optionalPerson
          .flatMap(person -> person.getOccupation())
          .filter(occupation -> occupation.equals("Computer Programmer"))
          .isPresent();
}

Conclusion

Null has been around since long before Java and other modern programming languages. And it is unlikely we can live without some representation for missing or “no data”. But there are ways to deal with it that don’t have to throw unexpected NPE errors. There are design patterns like Null Object and Special Case as well as more recent language-based solutions like Optionals that can help manage the problem. Used conscientiously, they can provide a large degree of null safety and, generally, promote good design practices and better API’s. That said, these aren’t the only, and perhaps not the best, ways of dealing with null. In a future blog post, I will discuss Kotlin’s approach null safety.