对我来说,这听起来像是初级到中级开发人员在某些时候往往会面临的一个相当普遍的问题:他们要么不知道,要么不信任他们正在参与的合约,并防御性地过度检查空值。此外,在编写自己的代码时,他们倾向于依赖返回空值来指示某些内容,因此要求调用者检查空值。
换句话说,有两种情况会出现空检查:
-
如果无效,则表示合同条款中的有效答复;和
-
它不是有效的响应。
(2)很容易。从 Java 1.7 开始你可以使用Objects.requireNonNull(foo) https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/util/Objects.html#requireNonNull(T)。 (如果您坚持使用以前的版本,那么assertions https://docs.oracle.com/javase/7/docs/technotes/guides/language/assert.html可能是一个不错的选择。)
此方法的“正确”用法如下所示。该方法返回传递给它的对象并抛出一个NullPointerException
如果对象为空。这意味着返回值始终为非空。该方法主要用于验证参数。
public Foo(Bar bar) {
this.bar = Objects.requireNonNull(bar);
}
它也可以像assert
但是,因为如果对象为空,它会抛出异常。在这两种用途中,都可以添加一条消息,该消息将显示在异常中。下面将其用作断言并提供消息。
Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();
通常会抛出特定的异常,例如NullPointerException
当一个值为空但不应该为空时有利于抛出更一般的异常,例如AssertionError
。这就是 Java 库所采用的方法;偏爱NullPointerException
over IllegalArgumentException
当参数不允许为空时。
(1) 有点难。如果您无法控制所调用的代码,那么您就会陷入困境。如果 null 是有效响应,则必须检查它。
但是,如果您确实控制了代码(通常是这种情况),那么情况就不同了。避免使用空值作为响应。使用返回集合的方法很简单:几乎总是返回空集合(或数组)而不是 null。
对于非收藏可能会更困难。以此为例:如果您有以下接口:
public interface Action {
void doSomething();
}
public interface Parser {
Action findAction(String userInput);
}
解析器获取原始用户输入并找到要做的事情,也许如果您正在为某些事情实现命令行界面。现在,您可以制定合同,如果没有适当的操作,则返回 null。这导致了您所说的空检查。
另一种解决方案是永远不返回 null,而是使用空对象模式 https://en.wikipedia.org/wiki/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;
}
}
}
Compare:
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();
}
to
ParserFactory.getParser().findAction(someInput).doSomething();
这是一个更好的设计,因为它可以使代码更简洁。
也就是说,也许 findAction() 方法抛出带有有意义的错误消息的异常是完全合适的——尤其是在依赖用户输入的情况下。 findAction 方法抛出异常比调用方法因简单的 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);
}
}
}