使用requireNonNull() 作为方法中的第一条语句可以立即/快速识别异常的原因。
堆栈跟踪清楚地表明,一旦进入方法,就会引发异常因为调用者不遵守要求/合同。
将null 对象传递给另一个方法可能确实会一次引发异常,但问题的原因可能更难以理解,因为异常将在@987654323 的特定调用中引发@ 可能更远的对象。
这是一个具体而真实的例子,它说明了为什么我们必须支持快速失败,尤其是使用Object.requireNonNull() 或任何方式对设计为非null 的参数执行无空检查。
假设一个Dictionary 类组成一个LookupService 和一个List 的String 表示包含在其中的单词。这些字段被设计为不是null,其中一个在Dictionary 中传递构造函数。
现在假设 Dictionary 的“错误”实现没有 null 检查方法条目(这里是构造函数):
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 参数提供 null 引用:
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);
线程“主”java.lang.NullPointerException 中的异常
在 LookupService.isFirstElement(LookupService.java:5)
在 Dictionary.isFirstElement(Dictionary.java:15)
在 Dictionary.main(Dictionary.java:22)
异常是在LookupService 类中触发的,而它的起源要早得多(Dictionary 构造函数)。它使整体问题分析变得不那么明显。
是wordsnull?是words.get(0) null 吗?两个都 ?
为什么一个、另一个或两者都是 null ?
Dictionary(构造函数?调用方法?)中的编码错误?是 LookupService 中的编码错误吗? (构造函数?调用的方法?)?
最后,我们将不得不检查更多代码以找到错误来源,并且在更复杂的类中甚至可能使用调试器来更容易地理解发生了什么。
但是为什么一件简单的事情(缺少空检查)会变成一个复杂的问题呢?
因为我们允许在较低组件上泄漏特定组件上的初始错误/缺失。
想象一下 LookupService 不是本地服务,而是远程服务或第三方库,调试信息很少,或者想象在检测到 null 之前,您没有 2 层而是 4 或 5 层对象调用?这个问题将更加复杂,难以分析。
所以偏爱的方法是:
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");
线程“主”java.lang.NullPointerException 中的异常
在 java.util.Objects.requireNonNull(Objects.java:203)
在 com.Dictionary.(Dictionary.java:15)
在 com.Dictionary.main(Dictionary.java:24)
请注意,这里我用构造函数说明了问题,但方法调用可能具有相同的非空检查约束。