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.
The Null object pattern is a design pattern that simplifies the use of dependencies that can be undefined. This is achieved by using instances of a concrete class that implements a known interface, instead of null references.
The intent of the Null Object Pattern is to minimize that kind of null check. Instead, we can identify the null behavior and encapsulate it in the type expected by the client code. More often then not, such neutral logic is very simple – do nothing.
A Null Object can be used to allow a Visitor to safely visit a hierarchy and handle the null situation. Null Object is a concrete collaborator class that acts as the collaborator for a client which needs one. The null behavior is not designed to be mixed into an object that needs some do nothing behavior.
The null object pattern provides a non-functional object in place of a null reference and therefore allows methods to be called on it. So, for some invalid input to the method GetMobileByName, our MobileRepository would return an instantiated, yet null, IMobile object in place of a null reference.
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 as a.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 that Car
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 two Car
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 type T
is, Optional<T>
is a monoid:
empty
, otherwise pick the second value":
x || empty = x
empty || x = x
Optional.empty()
, because Optional.empty().orElse(anything)
is the same as just anything
.So basically, Optional<T>
is a wrapper that adds a null object to types like Car
that don't have one. The Optional<T>.orElse(T value)
method that is a slightly refactored version of the "pick first non-empty
value" monoid.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With