一尘不染

在 Java 中避免 NullPointerException

javascript

我用object != null了很多来避免NullPointerException.

什么是替代方案:

if (someobject != null) {
    someobject.doCalc();
}

阅读 168

收藏
2022-01-10

共1个答案

一尘不染

对我来说,这听起来像是初级到中级开发人员在某些时候往往会面临的一个相当普遍的问题:他们要么不知道,要么不信任他们参与的合约,并且防御性地过度检查空值。此外,在编写自己的代码时,他们倾向于依赖返回空值来指示某些内容,从而要求调用者检查空值。

换句话说,有两种情况会出现空值检查:

  1. 如果 null 是合同中的有效响应;and
  2. 如果它不是有效的响应。

(2) 容易。使用assert语句(断言)或允许失败(例如 NullPointerException)。断言是 1.4 中添加的一项未被充分利用的 Java 功能。语法是:

assert <condition>

或者

assert <condition> : <object>

where<condition>是一个布尔表达式,<object>是一个对象,其toString()方法的输出将包含在错误中。

一个assert语句抛出一个ErrorAssertionError如果条件是不正确的)。默认情况下,Java 忽略断言。您可以通过将选项传递-ea给 JVM来启用断言。您可以为单个类和包启用和禁用断言。这意味着您可以在开发和测试时使用断言验证代码,并在生产环境中禁用它们,尽管我的测试表明断言几乎没有性能影响。

在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言就会发生这种情况。唯一的区别是,断言可能会发生得更快,以更有意义的方式发生,并且可能带有额外的信息,这可能会帮助您找出意外发生的原因。

(1) 有点难。如果您无法控制正在调用的代码,那么您就会陷入困境。如果 null 是有效响应,则必须检查它。

但是,如果您确实控制了代码(并且通常是这种情况),那么情况就不同了。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是空值。

对于非收藏,可能会更难。以此为例:如果您有这些接口:

public interface Action {
  void doSomething();
}

public interface Parser {
  Action findAction(String userInput);
}

Parser 获取原始用户输入并找到要做的事情,也许如果您正在为某事实现命令行界面。现在,如果没有适当的操作,您可以制定返回 null 的合同。这导致您正在谈论的空检查。

另一种解决方案是永远不要返回 null 而是使用Null 对象模式

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 方法抛出异常比调用方法抛出一个简单的 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);
        }
    }
}
2022-01-10