避免!=空语句

我使用object != null来避免NullPointerException

有没有一个很好的选择呢?

例如:

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

这可以避免NullPointerException ,如果该对象为null或未知,则不会发生。

请注意,接受的答案可能已过时,请参阅https://stackoverflow.com/a/2386013/12943以获取更新的方法。


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

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

  • 无效是合同方面的有效回应; 和

  • 它不是有效的答复。

  • (2)很容易。 使用assert语句(断言)或允许失败(例如,NullPointerException)。 断言是1.4中添加的一个使用率很低的Java特性。 语法是:

    assert <condition>
    

    要么

    assert <condition> : <object>
    

    其中<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 Object模式:

    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()方法来说,抛出一个有意义的错误消息的Exception是完全合适的 - 特别是在这种情况下,您依赖于用户输入。 findAction方法抛出一个Exception比调用方法抛出一个简单的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);
            }
        }
    }
    

    如果您使用(或计划使用)Java IDE(如JetBrains IntelliJ IDEA,Eclipse或Netbeans)或findbugs等工具,则可以使用注释来解决此问题。

    基本上,你有@Nullable@NotNull

    您可以在方法和参数中使用,如下所示:

    @NotNull public static String helloWorld() {
        return "Hello World";
    }
    

    要么

    @Nullable public static String helloWorld() {
        return "Hello World";
    }
    

    第二个例子不能编译(在IntelliJ IDEA中)。

    当您在另一段代码中使用第一个helloWorld()函数时:

    public static void main(String[] args)
    {
        String result = helloWorld();
        if(result != null) {
            System.out.println(result);
        }
    }
    

    现在IntelliJ IDEA编译器会告诉你这个检查是无用的,因为helloWorld()函数不会返回null

    使用参数

    void someMethod(@NotNull someParameter) { }
    

    如果你写了类似的东西:

    someMethod(null);
    

    这不会编译。

    最后一个使用@Nullable例子

    @Nullable iWantToDestroyEverything() { return null; }
    

    这样做

    iWantToDestroyEverything().something();
    

    你可以确定这不会发生。 :)

    这是让编译器检查比平时更多的东西,并强制您的合约变得更强大的好方法。 不幸的是,它并没有得到所有编译器的支持。

    在IntelliJ IDEA 10.5及以上版本中,他们添加了对任何其他@Nullable @NotNull实现的支持。

    查看博客文章更灵活和可配置的@ Nullable / @NotNull注释。


    如果空值不被允许

    如果你的方法是从外部调用的,那么从这样的事情开始:

    public void method(Object object) {
      if (object == null) {
        throw new IllegalArgumentException("...");
      }
    

    然后,在该方法的其余部分中,您将知道该object不为空。

    如果它是一个内部方法(不是API的一部分),只需记录它不能为null,就是这样。

    例:

    public String getFirst3Chars(String text) {
      return text.subString(0, 3);
    }
    

    但是,如果你的方法只传递了值,并且下一个方法将其传递,那么它可能会产生问题。 在这种情况下,你可能想要按照上面的方法检查参数。

    如果允许null

    这真的取决于。 如果发现我经常做这样的事情:

    if (object == null) {
      // something
    } else {
      // something else
    }
    

    所以我分支,做两件完全不同的事情。 没有丑陋的代码片段,因为根据数据我确实需要做两件不同的事情。 例如,我应该处理输入,还是应该计算一个很好的默认值?


    我真的很少使用这个成语“ if (object != null && ... ”。

    如果你展示了你通常使用习惯用法的例子,那么给你例子可能会更容易一些。

    链接地址: http://www.djcxy.com/p/225.html

    上一篇: Avoiding != null statements

    下一篇: How to horizontally center a <div> in another <div>?