协慌网

登录 贡献 社区

避免!= null 语句

我使用object != null来避免NullPointerException

有没有一个很好的替代品呢?

例如:

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

当不知道对象是否为null时,这可以避免NullPointerException

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

答案

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

换句话说,有两个实例进行空检查:

  1. 如果 null 是合同方面的有效回复; 和

  2. 哪里不是有效的回复。

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

assert <condition>

要么

assert <condition> : <object>

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

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

在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言将会发生这种情况。唯一的区别是,断言可能会更快地发生,以更有意义的方式发生,并且可能带有额外的信息,这可能会帮助您弄清楚如果您没有预料到它会发生的原因。

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

如果它是你控制的代码(然而通常就是这种情况),那么这是一个不同的故事。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是 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()方法完全适合抛出带有意义错误消息的异常 - 特别是在你依赖用户输入的情况下。对于 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 IDEAEclipseNetbeans或像 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 ,他们添加了对任何其他@Nullable @NotNull实现的支持。

请参阅博客文章更灵活和可配置的 @Nullable / @NotNull 注释

如果不允许空值

如果您的方法是外部调用的,请从以下内容开始:

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

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

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

例:

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

但是,如果您的方法只是传递了值,并且下一个方法将其传递给它,则可能会出现问题。在这种情况下,您可能需要检查上面的参数。

如果允许 null

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

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

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


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

如果您显示通常使用成语的示例,则可能更容易为您提供示例。

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

换句话说,有两个实例进行空检查:

  1. 如果 null 是合同方面的有效回复; 和

  2. 哪里不是有效的回复。

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

assert <condition>

要么

assert <condition> : <object>

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

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

在这种情况下不使用断言是可以的,因为代码只会失败,如果使用断言将会发生这种情况。唯一的区别是,断言可能会更快地发生,以更有意义的方式发生,并且可能带有额外的信息,这可能会帮助您弄清楚如果您没有预料到它会发生的原因。

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

如果它是你控制的代码(然而通常就是这种情况),那么这是一个不同的故事。避免使用空值作为响应。使用返回集合的方法,很容易:几乎一直返回空集合(或数组)而不是 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()方法完全适合抛出带有意义错误消息的异常 - 特别是在你依赖用户输入的情况下。对于 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 IDEAEclipseNetbeans或像 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 ,他们添加了对任何其他@Nullable @NotNull实现的支持。

请参阅博客文章更灵活和可配置的 @Nullable / @NotNull 注释

如果不允许空值

如果您的方法是外部调用的,请从以下内容开始:

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

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

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

例:

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

但是,如果您的方法只是传递了值,并且下一个方法将其传递给它,则可能会出现问题。在这种情况下,您可能需要检查上面的参数。

如果允许 null

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

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

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


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

如果您显示通常使用成语的示例,则可能更容易为您提供示例。