我使用x!=null以避免NullPointerException。还有其他选择吗?

if (x != null) {
    // ...
}

当前回答

首先,我们不能真正删除所有空条件。我们可以使用@NotNull和@Nullable注释来减少它们(如上所述)。但这需要一些框架的支持。这是OVal可以帮助的地方。

基本思想是对象/参数/构造函数应始终满足前提条件。您可以有很多先决条件,例如Nullable、NotNull和OVal。它们会注意对象在调用时应处于一致状态。

我猜OVal内部使用AspectJ来验证前提条件。

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

例如

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);

其他回答

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

换言之,有两种情况会出现空检查:

在合同条款中,null为有效响应;和如果它不是有效的响应。

(2) 很容易。从Java1.7开始,您可以使用Objects.requireOnNull(foo)

该方法的“正确”用法如下。该方法返回传递给它的对象,如果对象为空,则抛出NullPointerException。这意味着返回的值总是非空的。该方法主要用于验证参数。

public Foo(Bar bar) {
    this.bar = Objects.requireNonNull(bar);
}

它也可以像断言一样使用,因为如果对象为空,它会抛出异常。在这两种情况下,都可以添加一条消息,该消息将显示在异常中。下面将其用作断言并提供消息。

Objects.requireNonNull(someobject, "if someobject is null then something is wrong");
someobject.doCalc();

当值为null但不应为null时,通常会引发特定异常(如NullPointerException),这有利于引发更一般的异常(如AssertionError)。这是Java库采用的方法;当参数不允许为null时,支持NullPointerException而不是IllegalArgumentException。

(1) 有点难。如果你无法控制正在调用的代码,那么你就被卡住了。如果null是有效的响应,则必须检查它。

然而,如果是由你控制的代码(通常是这样),那就另当别论了。避免使用null作为响应。对于返回集合的方法,很容易:总是返回空集合(或数组)而不是空值。

对于非集合,这可能会更困难。举个例子:如果您有这些接口:

public interface Action {
  void doSomething();
}

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

其中Parser接收原始用户输入并找到要做的事情,如果您正在实现某个命令行接口。现在,如果没有适当的操作,您可能会使契约返回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();
}

to

ParserFactory.getParser().findAction(someInput).doSomething();

这是一个更好的设计,因为它导致了更简洁的代码。

也就是说,findAction()方法抛出带有有意义的错误消息的异常可能是完全合适的——尤其是在这种情况下,您依赖于用户输入。findAction方法抛出一个异常比调用方法用简单的NullPointerException(没有解释)爆炸要好得多。

try {
    ParserFactory.getParser().findAction(someInput).doSomething();
} catch(ActionNotFoundException anfe) {
    userConsole.err(anfe.getMessage());
}

或者,如果您认为try/catch机制太难看,而不是Do Nothing,那么您的默认操作应该向用户提供反馈。

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);
        }
    }
}

仅适用于这种情况-

在调用equals方法之前不检查变量是否为空(下面的字符串比较示例):

if ( foo.equals("bar") ) {
 // ...
}

如果foo不存在,将导致NullPointerException。

如果这样比较字符串,可以避免这种情况:

if ( "bar".equals(foo) ) {
 // ...
}

最终,彻底解决这个问题的唯一方法是使用不同的编程语言:

在Objective-C中,你可以在nil上调用一个方法,而绝对不会发生任何事情。这使大多数空检查变得不必要,但会使错误更难诊断。在Nice这一Java派生语言中,所有类型都有两个版本:可能为空的版本和非空的版本。只能对非空类型调用方法。通过显式检查null,可以将可能为null的类型转换为非null类型。这使得更容易知道哪些地方需要空检查,哪些地方不需要空检查。

首先,我们不能真正删除所有空条件。我们可以使用@NotNull和@Nullable注释来减少它们(如上所述)。但这需要一些框架的支持。这是OVal可以帮助的地方。

基本思想是对象/参数/构造函数应始终满足前提条件。您可以有很多先决条件,例如Nullable、NotNull和OVal。它们会注意对象在调用时应处于一致状态。

我猜OVal内部使用AspectJ来验证前提条件。

@Guarded
public class BusinessObject
{
  public BusinessObject(@NotNull String name)
  {
    this.name = name;
  }

  ...
}

例如

// Throws a ConstraintsViolatedException because parameter name is null
BusinessObject bo = new BusinessObject(null);

您可以使用FindBugs。他们还有一个Eclipse插件),可以帮助您查找重复的空检查(除其他外),但请记住,有时您应该选择防御性编程。还有Java合同可能会有所帮助。