我们的想法是快速失败。例如,考虑这个愚蠢的类:
public class Foo {
private final String s;
public Foo(String s) {
this.s = s;
}
public int getStringLength() {
return s.length();
}
}
假设您不希望允许 的空值。(否则将抛出NPE)。按照原样上课,当你发现它的时候,已经太晚了 - 很难找出是谁把它放在那里的。罪魁祸首很可能属于一个完全不同的阶级,而这个实例可能是很久以前构建的。现在,您必须梳理代码库,以找出谁可能在那里放置值。s
getStringLength
null
Foo
null
相反,想象一下这个构造函数:
public Foo(String s) {
this.s = checkNotNull(s);
}
现在,如果有人在那里放了一个,你会马上发现 - 并且你会看到堆栈跟踪准确地指向出错的调用。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;
}
}
调用将导致 NPE 被抛出 -- 但不是之前已递增。这可能不是你想要的;作为该类的用户,我可能期望如果它不接受nulls,那么如果你传递一个null,它的状态应该保持不变(换句话说:调用应该失败,但它不应该使对象无效)。显然,在此示例中,您也可以通过在递增之前获取来修复它,但是您可以看到对于更长且更复杂的方法,首先检查所有参数是否有效,然后才修改状态可能很有用:accept(null)
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();
}