这个想法是快速失败。例如,考虑这个愚蠢的类:
public class Foo {
private final String s;
public Foo(String s) {
this.s = s;
}
public int getStringLength() {
return s.length();
}
}
假设您不想为s
. (否则getStringLength
会抛出 NPE)。照原样上课,当你发现null
它时,已经太晚了——很难找出是谁把它放在那里的。罪魁祸首很可能在一个完全不同的类中,并且该Foo
实例可能是很久以前构建的。现在你必须梳理你的代码库,找出谁可能在null
那里设置了值。
相反,想象一下这个构造函数:
public Foo(String s) {
this.s = checkNotNull(s);
}
现在,如果有人把 a放在那里,你会马上null
发现——你会得到堆栈跟踪,准确地指向出错的调用。
另一个有用的情况是,如果您想在采取可以修改状态的操作之前检查参数。例如,考虑这个类,它计算它得到的所有字符串长度的平均值:
public class StringLengthAverager {
private int stringsSeen;
private int totalLengthSeen;
public void accept(String s) {
stringsSeen++;
totalLengthSeen += s.length();
}
public double getAverageLength() {
return ((double)totalLengthSeen) / stringsSeen;
}
}
调用accept(null)
将导致 NPE 被抛出——但不是在stringsSeen
增加之前。这可能不是您想要的;作为该类的用户,我可能期望如果它不接受空值,那么如果传递空值,它的状态应该保持不变(换句话说:调用应该失败,但它不应该使对象无效)。显然,在此示例中,您也可以通过s.length()
在递增之前获取来修复它stringsSeen
,但是您可以看到对于更长和更复杂的方法,首先检查所有参数是否有效,然后才修改状态可能很有用:
public void accept(String s) {
checkNotNull(s); // that is, s != null is a precondition of the method
stringsSeen++;
totalLengthSeen += s.length();
}