There seems to be a growing community of people saying that you should never return null and should always use the Null Object Pattern instead. I can see the usefullness of the NOP when using a collection/map/array or calling boolean functions such as isAuthenticated(), which is shown here.
I haven't found anything on this that is fully convincing. Bear with me here as I try to organize my thoughts.
My understanding is that instead of returning a null object, you return a valid object that has been "zeroed" out.
So for example, the client would make a call to get an object:
Car car = getCar();
If not using the NOP you would need to check if the object returned from getCar() is null before calling any methods on it:
if (car != null){
color = car.getColor();
doScreenStuff(color);
}
Using the NOP, instead of getCar()
returning null, it now returns an Object that has been effectively "zeroed out". So now we no longer need to do if (car != null)
and can just request the color. So, I suppose that our "zeroed out" object would return "none" when we call color.
How does this help? It seems that moving forward and calling methods on an empty object causes just as much pain as just checking null. Now, when it comes time to display the information, we need to check that the color isn't "none", that the height isn't 0, or whatever other values you have. So essentially, instead of checking in the beginning of processing if the car is null, you check afterwards if the car object we have is a real car or a substitute. I.E. we don't want to display a bunch of empty objects, so we need some way to filter out all of our empty objects.
This filtering is an added step just like calling if (car != null). The only difference is that with checking null, we can stop processing as soon as we discover that the car object is null by throwing an exception, whereas with NOP we call methods on the empty object and keep chugging along until it gets to be time to display the object and at this point we filter out the empties. Furthermore, you need to know the values returned by your empty object. I.E. does getColor() return "none" or "empty".
There obviously must be something I'm overlooking. Thanks in advance.
MattPutnam's answer is right on point, and I second it. I'd add this: the concept of "null object," when you analyze it, seems to boil down to the mathematical concept of a monoid. You can think of it this way: a monoid is a type that has both of these things:
a.op(b).op(c)
is the same asa.op(b.op(c))
.The classic example of the null object pattern is to return an empty list or array instead of null. Well, lists are a monoid, with append as the operation and the empty list as the neutral element.
Now, the problem that you face in your
Car
example is thatCar
isn't really a monoid; there is no notion of "the empty car" or "the neutral car", and there isn't really a sensible operation that you could use to combine twoCar
s into one.So the recommendation you're rightly getting is to use something like the Java 8
Optional
. And the trick is that no matter what typeT
is,Optional<T>
is a monoid:empty
, otherwise pick the second value":x || empty = x
empty || x = x
Optional.empty()
, becauseOptional.empty().orElse(anything)
is the same as justanything
.So basically,
Optional<T>
is a wrapper that adds a null object to types likeCar
that don't have one. TheOptional<T>.orElse(T value)
method that is a slightly refactored version of the "pick first non-empty
value" monoid.