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

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

当前回答

我尝试过NullObjectPattern,但对我来说并不总是最好的方法。有时,“不采取行动”并不合适。

NullPointerException是一个运行时异常,这意味着它是开发人员的错,并且凭借足够的经验,它会准确地告诉您错误在哪里。

现在来回答:

尽量将所有属性及其访问器设为私有,或者避免将它们暴露给客户端。当然,您可以在构造函数中设置参数值,但通过缩小范围,您不会让客户端类传递无效值。如果需要修改值,可以始终创建新对象。您只检查构造函数中的值一次,在其他方法中,您几乎可以确定这些值不为空。

当然,经验是理解和应用这一建议的更好方式。

字节

其他回答

我高度无视建议在任何情况下使用空对象的答案。这种模式可能会破坏合同,将问题埋得越来越深,而不是解决问题,更不用说使用不当会产生另一堆需要未来维护的样板代码。

实际上,如果从方法返回的某个值可以为空,并且调用代码必须对此做出决定,那么应该有一个更早的调用来确保状态。

还请记住,如果不小心使用,空对象模式将占用内存。为此,NullObject的实例应该在所有者之间共享,而不是每个所有者的unigue实例。

此外,我不建议在类型是原始类型表示的情况下使用这种模式,比如数学实体,它们不是标量:向量、矩阵、复数和POD(普通旧数据)对象,它们是用来以Java内置类型的形式保存状态的。在后一种情况下,您将以任意结果调用getter方法。例如,NullPerson.getName()方法应该返回什么?

为了避免荒谬的结果,值得考虑这样的案例。

避免不必要的空检查的方法很简单:

您需要知道哪些变量可以为空,哪些变量不能为空,并且您需要对给定变量属于哪一类有信心。

但是,尽管它可以说得很简单,但实现它却很困难。关键在于自信的部分,因为你如何确定变量不能为空?

对此没有快速的解决方法,但这里有一些提示:

干净的代码。能够推理一段代码的行为最重要的一点是,它是以易于理解的方式编写的。根据变量所代表的内容命名变量,根据它们所做的内容命名方法,应用单一责任原则(SOLID中的S:http://en.wikipedia.org/wiki/SOLID_(object-oriented_design),这意味着每一段代码都应该有一个单独的责任,并且不做任何其他事情)。一旦你的代码是干净的,那么你就更容易理解它,也更容易理解多个代码层。对于杂乱的代码,试图理解一个方法的作用可能会让你忘记当初为什么要读这个方法。(提示:阅读罗伯特·C·马丁的《清洁代码》)避免返回空值。如果空值会使程序无法正常运行,请改为抛出异常(确保添加适当的错误处理)。返回空值可能是可接受的情况,例如,尝试从数据库中获取对象。在这些情况下,编写处理空值的代码,并在您的耳朵后面记下,这里有可能返回空值的内容。处理返回的null值,使其尽可能接近返回null的方法的调用方(不要盲目地将其传递回调用链)永远不要将显式空值作为参数传递(至少不要跨类传递)。如果您曾经处于传递空参数是唯一选项的位置,那么创建一个不包含此参数的新方法是一种方法。验证您的输入!确定应用程序的“入口点”。它们可以从Web服务、REST服务、远程EJB类、控制器等所有内容。对于这些入口点中的每个方法,问问自己:“如果此参数为空,此方法是否正确执行?”如果答案为否,请添加Validate.notNull(someParam,“当someParam为空时无法运行!”);。如果缺少必需的参数,这将引发IllegalArgumentException。在入口点进行这种类型的验证的好处是,您可以很容易地在从入口点执行的代码中假设该变量永远不会为空!此外,如果这失败了,在入口点,调试会比代码深处出现NullPointerException要容易得多,因为这样的失败只能意味着一件事:客户端没有向您发送所有必需的信息。在大多数情况下,您希望验证所有输入参数,如果您发现自己处于需要允许大量空值的位置,这可能是接口设计不良的标志,需要进行重构/添加以满足客户机的需要。使用集合时,返回一个空集合而不是空集合!使用数据库时,请使用非空约束。这样,您就知道从数据库中读取的值不能为空,并且不必检查它。构造代码并坚持执行。这样做可以让您对代码的行为做出假设,例如,如果应用程序的所有输入都经过验证,那么您可以假设这些值永远不会为空。如果您还没有这样做,请编写代码的自动测试。通过编写测试,您将对代码进行推理,并且您也将更加确信它确实做到了它应该做的事情。此外,自动化测试通过让您立即知道这段代码没有做以前的事情来防止重构过程中的错误。

当然,您仍然需要进行空检查,但它可以降到最低限度(即,知道您可能会得到空值,而不是到处都是空值)。当谈到空检查时,我实际上更喜欢使用三元运算符(但要小心使用,当您开始嵌套它们时,它们会变得非常混乱)

public String nullSafeToString(final Object o) {
    return o != null ? o.toString() : "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);
        }
    }
}

无论您在何处传递数组或向量,请将它们初始化为空值,而不是空值。-这样可以避免大量检查null,一切都很好:)

public class NonNullThing {

   Vector vectorField = new Vector();

   int[] arrayField = new int[0];

   public NonNullThing() {

      // etc

   }

}

哇,当我们有57种不同的方式来推荐NullObject模式时,我几乎不想再加一个答案,但我想,一些对这个问题感兴趣的人可能想知道,有人提议为Java7添加“空安全处理”,这是一种精简的语法,如果不等于空逻辑的话。

Alex Miller给出的示例如下:

public String getPostcode(Person person) {  
  return person?.getAddress()?.getPostcode();  
}  

这个表示仅在左标识符不为空时取消引用,否则将表达式的其余部分求值为空。一些人,如Java Pose成员迪克·沃尔(Dick Wall)和德文郡的选民,真的很喜欢这个提议,但也有人反对,理由是它实际上会鼓励更多地使用null作为前哨值。


更新:在Project Coin下提交了一份关于Java 7中空安全运算符的官方提案。语法与上面的示例稍有不同,但概念相同。


更新:空安全运营商提议未纳入Project Coin。因此,您不会在Java7中看到这种语法。