Lecture
Let's look at an example of using NULL in Java

What's wrong with this method? It can return NULL instead of an object – that's what's wrong. Using NULL is a horrible practice in OOP, and it should be avoided by all means. Plenty of different opinions have already been published on this subject, including Tony Hoare's presentation “Null References: The Billion Dollar Mistake” and David West's entire book “Object Thinking”. Here I will try to summarize all the arguments and show examples of how to avoid using NULL by replacing it with suitable object-oriented constructs. First, let's look at two possible alternatives to NULL.
The first is the “Null Object” design pattern (best implemented using a constant):

The second possible alternative is “fail fast” by throwing an exception in case it is impossible to return an object:

In systems design, a fail-fast system is one that immediately reports, at its interface, any condition that is likely to indicate a failure. Fail-fast systems are usually designed to stop normal operation rather than proceed with a possibly flawed process.
Now let's get acquainted with the arguments against using NULL. Before writing this post, in addition to the aforementioned presentation by Tony Hoare and the book by David West, I familiarized myself with a whole range of publications. These are “Clean Code” by Robert Martin, “Code Complete” by Steve McConnell, “Say “No” to NULL” by John Sonmez, and a discussion on StackOverflow titled “Is returning NULL a bad practice?”
Manual error handling
Every time you receive an object as input, you must check whether it is a reference to a valid object or a NULL. If you forget to check, your program may be interrupted right during execution by a thrown NullPointerException (NPE). Because of this, your code starts filling up with numerous checks and if/then/else branches.

This is exactly how exceptional situations should be handled in C and other strictly procedural programming languages. In OOP, exception handling was introduced primarily in order to get rid of manually written handling blocks. In OOP we let exceptions bubble up until they reach the application-wide error handler, and thanks to this our code becomes much cleaner and shorter:

Consider NULL references a relic of procedural-style programming, and use
1) Null Objects, or
2) Exceptions instead of them.
Ambiguous understanding
To accurately convey in its name what is happening, the method getByName() should be renamed to getByNameOrNullIfNotFound(). The same should be done for every method that returns an object or NULL, otherwise ambiguity cannot be avoided when reading the code.
Thus, in order for method names to be precise, you have to give methods longer names. To avoid ambiguity, always return a real object, a null object, or throw an exception. Someone might object that sometimes we simply need to return NULL in order to achieve the desired result. For example, the get() method of the Map interface in Java returns NULL when there are no more objects in the Map.

Thanks to the use of NULL in Map, this code only needs a single search loop to get the result. If we rewrite Map so that the get() method throws an exception in case nothing is found, our code will look like this:

Obviously, this method is twice as slow as the original one. So what should we do? There is a design flaw in the Map interface (no offense intended to its developers). Its method get() should have returned an Iterator, and then our code would look like this:

By the way, this is exactly how the STL method map::find() is designed in C++.
Computer thinking versus object-oriented thinking
The line of code if (employee == null) is quite understandable to someone who knows that an object in Java is a pointer to a data structure, and NULL is a pointer to nothing (0x00000000 on Intel x86 processors). However, if you start thinking in an object-oriented way, this line becomes much less meaningful. Here is what our code looks like from an object point of view:
That last question sounds a bit odd, doesn't it? If instead, after your request to speak to Jeffrey, the other end simply hangs up, that would cause us certain difficulties (an Exception). In that case we could try calling back, or report to our boss that we were unable to speak with Jeffrey, and finish our main task. Alternatively, the other side might offer to let you speak with someone else, who, although not Jeffrey, can either help you with most of your questions or refuse to help if we need to know something that only Jeffrey knows (a Null Object).
Slow failure
Instead of failing fast, the code above tries to die slowly, taking others down with it along the way. Instead of letting everyone know that something went wrong and exceptional handling needs to start immediately, it tries to hide its failure from the client. This is very similar to the manual exception handling we talked about above. Making your code as fragile as possible and letting it break when necessary is good practice. Make your methods extremely demanding about the data they work with. Let them complain, by throwing exceptions, if the data they were given is insufficient, or if the data simply isn't suitable for use in that method according to the intended scenario. Otherwise, return a Null Object that behaves in some generally accepted way and throws exceptions in all other cases.

Mutable and incomplete objects
In general, it is strongly recommended to design objects so that they are immutable. This means an object should receive all the necessary data when it is created and should never change its state throughout its entire lifecycle. NULL values are very often used in the “Lazy Loading” design pattern in order to make objects incomplete and mutable. Example:

Despite the fact that this technique is widespread, it is an anti-pattern for OOP. Mainly because it forces the object to bear responsibility for performance problems of the computing platform, which is exactly the kind of thing the Employee object cannot be aware of. Instead of managing its own state and behaving in a way appropriate to its purpose, the object is forced to worry about caching its own results – that's what “lazy loading” leads to. And caching is not at all what an employee does in an office, is it? The way out? Don't use “lazy loading” in such a primitive way as in the example above. Instead, move the caching concerns to another layer of your application. For example, in Java you can use the capabilities of aspect-oriented programming. For instance, jcabi-aspects has the @Cacheable annotation, which caches the value returned by a method

I hope this analysis was convincing enough for you to stop NULLifying your code :) The original article is here.
You might also be interested in topics such as:
Comments