In the programming field, there are a lot of wrong inventions. Null
References is one of them. Java introduced this invention in the initial version, which led many developers to develop the bad habit of using null mindlessly. Although Optional
was used to replace Null
after Java 8, the bad habit of using Null
cannot be replaced.
“I call it my billion-dollar mistake. It was the invention of the
Null
reference in 1965. At that time, I was designing the first comprehensive type system for references in an object-oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn’t resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.”— Tony Hoare (Turing Award winner)
- Related video: Null References: The Billion Dollar Mistake
What is Null
in Java?
In Java, Null
is a special value that can be assigned to object references. It represents the absence of an object or a default value.
It is neither an object nor a type, which is a common misconception that newcomers to the Java language must overcome.
Why Null
is A Bad Invention?
Null
as a “bad invention” in Java, and indeed in many other programming languages, is a perspective shared by many software engineers and computer scientists, most notably Tony Hoare, who invented the null reference. Hoare himself has referred to the invention of the Null
reference as his “billion-dollar mistake”.
It has the following drawbacks:
-
Null Pointer Exceptions: One of the most common causes of runtime errors in Java is the NullPointerException. This occurs when a program attempts to use an object reference that has been assigned null. Such errors can lead to system crashes and are often only discovered during runtime. Any lack of null pointer checking can make programs out of control.
-
Reliability: The presence of null can make software less reliable. Since any reference can potentially be
Null
, developers must constantly check for null to avoid exceptions, which adds verbosity and complexity to the code. -
Ambiguity:
Null
can sometimes represent multiple meanings, such as uninitialized, absence of a value, or an error condition. This ambiguity can lead to confusion and errors in understanding code. In most cases, the caller has no idea about the intention of the method to returnNull
.
In Java, due to the existence of
Null
:
- our programs often crash because of unpredictable
NullPointerException
;- our code is filled with lots of meaningless null checks;
- we are often confused by the
Null
return from methods.
“When we return
null
, we are essentially creating work for ourselves and foisting problems upon our callers. All it takes is one missingnull
check to send an application spinning out of control.”— Robert C. Martin
Try to Avoid Using Null
Modern programming languages and updates to existing languages have introduced features to mitigate the problems caused by Null
. For example, Java 8 introduced Optional
to represent the presence or absence of a value.
In Java, instead of returning Null
, we can throw an exception or return Optional
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Returning null (not recommended)
public User findUser(String userId) {
//...
return null;
}
// Throwing an exception (using for error condition)
public User findUser(String userId) {
//...
throw new UserNotFoundException("User not found with ID: " + userId);
}
// Returning an optional (using for absence of the value)
public Optional<User> findUser(String userId) {
//...
return Optional.empty();
}
Example of Using Optional
to replace Null
I can provide a simpler example to show the difference between Optional
and Null
.
Suppose we have a method that tries to retrieve a user’s email address from a database, but there is a chance that the corresponding user will not be found.
Example of Null
:
1
2
3
4
5
6
7
8
public String getEmailOfUser(String userId) {
User user = findUser(userId); // This method might return null
if (user != null) {
return user.getEmail();
} else {
return "Email not found";
}
}
Example of Optional
:
1
2
3
4
public String getEmailOfUser(String userId) {
Optional<User> user = findUser(userId);
return user.map(User::getEmail).orElse("Email not found");
}
The advantage of Optional
:
- Code Conciseness: In the Optional example, we avoid the traditional
if-else
structure, making the code more concise and readable. We also avoid redundant null checks. - Null Safety: By using Optional, we can handle potential null values more safely. We reduce the risk of
NullPointerException
, which can make programs out of control. - Functional Style: The use of Optional supports a functional programming approach, making the code style more modern and fluent.
If a method needs to return empty values, use Optional. This can avoid
NullPointerException
and meaningless null checking. It also reminds the caller that there may be empty values here.Please don’t use
Null
unless you have strong reasons for it.
When I worked at Huawei, colleagues in my group didn’t follow this rule very well. This leads to our project crashing often due to null pointers, and the error logs were full of
NullPointerExceptions
.And every time I did code review, my colleagues would always remind me if I did null-pointer checking on the variables. It was really annoying and time-wasting.
So, please don’t use
Null
.