I think you are missing several other options. For example, you could use a language which doesn't have
null. Haskell, Ruby, Smalltalk, many others simply don't have null references, so the problem doesn't even arise.
Another option would be to use a language which does have
null but has explicit
null tracking that allows you to statically ensure that you can never have null references. Spec# does that, for example.
A variant of that option is to use a language which doesn't have
null tracking, but use an external static analyzer, maybe aided by annotations. Code Contracts.NET (an offshoot of Spec#) does that, for example.
Yet another option is to encapsulate the notion of
nullness into a type. One of the problems with null references is that null references are really overloaded. What does getting a
null mean? Some things are meant to fail, e.g. parsers, or looking up a key in a dictionary. It is normal for parsers to be handed invalid input, it is normal to not find a key in a dictionary, that's not an error, and not an exceptional situation.
For this latter case, you can use an
Option type. An
Option is basically kind-of like a collection that is either empty or has exactly one element. (This analogy is actually pretty profound as we shall see shortly.) In the case of the dictionary example, a
Dictionary<K, V> would have a method
Option<V> get(K key) which returns either a "filled"
Option with the value, if the key exists, or an "empty"
Option if the key doesn't exist. The important thing is that the type system can ensure that you have to deal with both cases in your code.
Except that you often actually don't have to deal with both cases. That's where the analogy to collections comes in: an
Option is isomorphic to a list / an array / an iterator / a stream / an
IEnumerable / some other kind of collection which is either empty or holds exactly one value. This means that
Option can implement whatever collections interfaces your language has, can be a member of the collections framework and can be used with all the collections methods. For example, if you want to transform the value, then you don't need to actually get the value out of the
Option. All collections frameworks have a method to apply a transformation function to every element of the collection (
map in most languages,
collect in Smalltalk, Objective-C and Ruby,
select in SQL and .NET,
transform in C++). What does
map do when you call it on an empty collection? It returns an empty collection. And on a non-empty collection it will return another collection with each element (or in this case the only element) transformed. You don't even have to check whether or not your
Option is empty, you can just call
map on it, and it will do the right thing.
You can chain multiple
maps. You can use
SelectMany in .NET) to chain the use of
Options. You can use
accumulate) to get the value out or alternatively supply a default. And so on.
Apart from being a collection,
Option is also a monad, this makes it easy to work with in languages that have special syntactic support for monadic operations (e.g. Haskell's
for-comprehensions, C#'s and VB.NET's LINQ query comprehensions).
In Haskell, it is called
Maybe a and has two data constructors called
Just a and
Nothing. (If you are a Java person read
Just<T> and think of data constructors as final subclasses.) In Scala, it is called
Option[T] and has two subclasses
None. Even Java has one, it is called
Obviously, there is a difference between a language like Haskell, which doesn't have
null in the first place, and a language like Java, where
Optional<T> is an afterthought. In the first case,
nulls cannot happen, period. In the second case, I would treat
null like any other kind of invalid data: intercept it at the outer boundary of your system, and either throw an exception or sanitize it into an
Optional<T>. (The really nasty thing about a language like Java is of course that a field of type
Optional<T> may itself be
In Scala, for example,
null mainly exists for compatibility reasons. Pretty much the only way that you can end up with a
null from the Scala language, is with a declared but not initialized mutable field. However, mutable fields are rarely used in Scala, and very un-idiomatic. No method in the Scala library will ever return
null. Idiomatic Scala code will never return
null. Usually, the only way to end up with
null is by calling Java code, and that is typically handled the way I described above: you have a Scala wrapper for the Java code which handles all potential
nulls and the rest of the code uses that Scala abstraction and doesn't bother with
null at all.
Yet another option that is however not applicable in all cases is to use a special "empty" object to denote the absence of data instead of
null. A dummy user account for a
User field, an empty string for a
String field, an empty list for a
List field and so on.