Using requireNonNull()
as first statements in a method allow to identify right now/fast the cause of the exception.
The stacktrace indicates clearly that the exception was thrown as soon as the entry of the method because the caller didn't respect the requirements/contract.
Passing a null
object to another method may indeed provoke an exception at a time but the cause of the problem may be more complicated to understand as the exception will be thrown in a specific invocation on the null
object that may be much further.
Here is a concrete and real example that shows why we have to favor fail fast in general and more particularly using Object.requireNonNull()
or any way to perform a no null check on parameters designed to be not null
.
Suppose a Dictionary
class that composes a LookupService
and a List
of String
representing words contained in. These fields are designed to be not null
and one of these is passed in the Dictionary
constructor.
Now suppose a "bad" implementation of Dictionary
without null
check in the method entry (here that is the constructor):
public class Dictionary {
private final List<String> words;
private final LookupService lookupService;
public Dictionary(List<String> words) {
this.words = this.words;
this.lookupService = new LookupService(words);
}
public boolean isFirstElement(String userData) {
return lookupService.isFirstElement(userData);
}
}
public class LookupService {
List<String> words;
public LookupService(List<String> words) {
this.words = words;
}
public boolean isFirstElement(String userData) {
return words.get(0).contains(userData);
}
}
Now, let's invoke the Dictionary
constructor with a null
reference for the words
parameter :
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
The JVM throws the NPE at this statement :
return words.get(0).contains(userData);
Exception in thread "main" java.lang.NullPointerException
at LookupService.isFirstElement(LookupService.java:5)
at Dictionary.isFirstElement(Dictionary.java:15)
at Dictionary.main(Dictionary.java:22)
The exception is triggered in the LookupService
class while the origin of it is well earlier (the Dictionary
constructor). It makes the overall issue analysis much less obvious.
Is words
null
? Is words.get(0) null
? Both ?
Why the one, the other or maybe both are null
?
Is it a coding error in Dictionary
(constructor? invoked method?) ? Is it a coding error in LookupService
? (constructor? invoked method?) ?
Finally, we will have to inspect more code to find the error origin and in a more complex class maybe even use a debugger to understand more easily what it happened.
But why a simple thing (a lack of null check) become a complex issue ?
Because we allowed the initial bug/lack identifiable on a specific component leak on lower components.
Imagine that LookupService
was not a local service but a remote service or a third party library with few debugging information or imagine that you didn't have 2 layers but 4 or 5 layers of object invocations before that the null
be detected ? The problem would be still more complex to analyse.
So the way to favor is :
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
In this way, no headache : we get the exception thrown as soon as this is received :
// exception thrown early : in the constructor
Dictionary dictionary = new Dictionary(null);
// we never arrive here
boolean isFirstElement = dictionary.isFirstElement("anyThing");
Exception in thread "main" java.lang.NullPointerException
at java.util.Objects.requireNonNull(Objects.java:203)
at com.Dictionary.(Dictionary.java:15)
at com.Dictionary.main(Dictionary.java:24)
Note that here I illustrated the issue with a constructor but a method invocation could have the same non null check constraint.