在我看来,这听起来像是初级到中级开发人员在某些时候往往会面临的一个相当普遍的问题:他们要么不知道,要么不信任他们参与的合约,并且防御性地过度检查空值。此外,在编写自己的代码时,他们倾向于依靠返回空值来指示某些内容,因此需要调用者检查空值。
换句话说,有两种情况会出现空值检查:
其中 null 是合同条款中的有效响应;和
如果不是有效响应。
(2) 很简单。使用assert 语句(断言)或允许失败(例如,NullPointerException)。断言是在 1.4 中添加的一个未被充分利用的 Java 特性。语法是:
assert <condition>
或
assert <condition> : <object>
其中<condition> 是一个布尔表达式,<object> 是一个对象,其toString() 方法的输出将包含在错误中。
如果条件不成立,assert 语句将引发 Error (AssertionError)。默认情况下,Java 忽略断言。您可以通过将选项 -ea 传递给 JVM 来启用断言。您可以启用和禁用单个类和包的断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试显示断言几乎不会影响性能。
在这种情况下不使用断言是可以的,因为代码只会失败,如果你使用断言就会发生这种情况。唯一的区别是,如果使用断言,它可能会以更有意义的方式更快地发生,并且可能带有额外的信息,这可能会帮助您弄清楚如果您没有预料到它为什么会发生。
(1) 有点难。如果您无法控制所调用的代码,那么您将陷入困境。如果 null 是一个有效的响应,你必须检查它。
但是,如果是您控制的代码(这种情况经常发生),那就另当别论了。避免使用空值作为响应。使用返回集合的方法,这很容易:几乎总是返回空集合(或数组)而不是 null。
使用非收藏品可能会更难。以此为例:如果您有这些接口:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
Parser 获取原始用户输入并找到要做的事情,也许如果您正在为某事实现命令行界面。现在,如果没有适当的操作,您可能会制定返回 null 的合同。这会导致您正在谈论的空值检查。
另一种解决方案是永远不要返回 null,而是使用 Null Object pattern:
public class MyParser implements Parser {
private static Action DO_NOTHING = new Action() {
public void doSomething() { /* do nothing */ }
};
public Action findAction(String userInput) {
// ...
if ( /* we can't find any actions */ ) {
return DO_NOTHING;
}
}
}
比较:
Parser parser = ParserFactory.getParser();
if (parser == null) {
// now what?
// this would be an example of where null isn't (or shouldn't be) a valid response
}
Action action = parser.findAction(someInput);
if (action == null) {
// do nothing
} else {
action.doSomething();
}
到
ParserFactory.getParser().findAction(someInput).doSomething();
这是一个更好的设计,因为它导致代码更简洁。
也就是说,findAction() 方法抛出一个带有有意义的错误消息的异常可能是完全合适的——尤其是在这种依赖用户输入的情况下。 findAction 方法抛出 Exception 比调用方法抛出一个简单的 NullPointerException 而没有任何解释要好得多。
try {
ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
userConsole.err(anfe.getMessage());
}
或者,如果您认为 try/catch 机制太丑陋,而不是什么都不做,您的默认操作应该向用户提供反馈。
public Action findAction(final String userInput) {
/* Code to return requested Action if found */
return new Action() {
public void doSomething() {
userConsole.err("Action not found: " + userInput);
}
}
}