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.
下面是一个具体而真实的例子,它展示了为什么我们必须在一般情况下支持快速失败,特别是使用Object.requireNonNull()或任何方式对设计为非空的参数执行无空检查。
假设有一个Dictionary类,它组成一个LookupService和一个String列表,表示包含在。这些字段被设计为非空,其中一个字段被传递给Dictionary构造函数。
现在假设Dictionary有一个“糟糕的”实现,在方法条目中没有空检查(这里是构造函数):
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);
}
}
现在,让我们调用Dictionary构造函数,对words形参使用空引用:
Dictionary dictionary = new Dictionary(null);
// exception thrown lately : only in the next statement
boolean isFirstElement = dictionary.isFirstElement("anyThing");
JVM在下面的语句中抛出NPE:
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.
所以帮忙的方法是:
public Dictionary(List<String> words) {
this.words = Objects.requireNonNull(words);
this.lookupService = new LookupService(words);
}
这样就不会让人头疼了:只要接收到这个,我们就会抛出异常:
// 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)
注意,这里我用构造函数说明了这个问题,但方法调用可以有相同的非空检查约束。