避免在Java中使用!= null的好处是什么?

4
我已经阅读了这个主题:避免使用!= null语句,但是我没有看到它的好处。
  1. 如果你返回一个空数组,你需要检查“真正”的数组在哪里,以及空数组在哪里。

  2. 不要使用像getName() != null这样的代码,你必须使用getName().length > 0。例如,我正在开发一个项目,在这个项目中我需要用几个压缩器压缩一些数据,并决定选择体积最小的压缩数据。如果我从压缩器“compress”方法中返回null,我需要检查返回值是否为null。如果我决定返回一个空数组,那么我还需要检查它是否为空。

我的理解正确吗?


我反对完全避免某些事物,除非已经证明它们完全不能使用。在这种情况下,如果空数组 / 列表和 null 传达不同的含义,则应使用 null。如果 null 和空数组 / 列表本身并没有传达特殊的含义,那么它们可以互换使用以使代码更清晰。 - nhahtdh
我不理解你的第一点。什么是“真实”的数组?如果你返回null,那应该就是真实的数组。如果这是一个错误情况,就抛出异常。 - Alexis King
@JakeKing,你看到我的例子了吗?我有一个压缩函数,如果该函数返回空数组,则解压函数将失败,因为它的实现需要先检查其第一个字节,所以我需要检查这种情况。 - Avershin Dmitry
对于不仅在互联网上阅读模式并将其作为真理而不问原因的问题,给予+1。人们可能会对像“null”这样的事情变得非常教条。 - yshavit
2
@AvershinDmitry 是的,但是根本的解决方案不是在这种情况下返回 null。如果您从压缩函数收到 null,则表示出了问题。如果有错误,请抛出异常。 - Alexis King
3个回答

11

使用空集合或“空白”操作而不是null的主要优点是,大多数情况下,这些对象在代码中仍然可以正常工作,无需进一步修改。null值本质上更容易出现错误。

例如,考虑以下代码:

String[] names = data.getNames();
if (names != null) {
    for (String name : names) {
        // Do stuff
    }
}

如果不检查null,则会出现NPE错误。使用标准for循环不能解决该问题。另一方面,如果您知道始终会获得某种类型的数组,则代码将在无需额外检查的情况下正常工作。如果数组为空,则根本不会运行循环。问题得到解决。

对于实现某种操作的代码也是如此。另一个例子:

Action myAction = data.getActionToRun();
if (myAction != null) {
    myAction.run();
}

再一次,你需要进行 null 检查。如果保证操作是存在的,那么你可以始终调用 action.run() 而不会产生任何副作用,然而空白操作只会什么都不做。就是这么简单。

在许多情况下,如果修改方法的返回方式,null 检查可以被简单地丢弃,从而导致更简单和易于理解的代码。在某些情况下,返回 null 是正确的选择(例如,从键值对集合中获取对象),因为没有默认的“无操作”值。但是,null 表示根本没有值,并且需要接收者进行额外处理。使用空白的、无操作的、非空对象允许数据对象处理错误。这是良好的封装。这是良好的编程。它只是有效。™

最后,返回 null 绝对不是处理错误的好方法。如果在您的代码中出现了一些应该不会出错的情况(除非您作为程序员犯了一个编程错误),请使用 assert 或异常。这些是失败。不要将 null 用作失败情况,而是将其用作简单缺少值的情况。


非常有帮助,谢谢。这意味着在我的示例中,当压缩函数无法压缩时最好返回空数组,并在将数据添加到列表之前检查此数据以确保不为空,以避免 NPE。 - Avershin Dmitry
2
@AvershinDmitry 这并不一定是最好的解决方案。在我看来,该方法应该永远不会返回null值(或空数组),除非出现错误。这不是一个好主意。如果出现错误,请抛出异常。 - Alexis King

7

有时候,返回一个空数组是更自然的对象。考虑以下代码:

String [] elements = getElements();

for (String element : elements)
   System.out.println(element);

如果返回一个空数组(也就是没有元素),将不会打印任何内容并且代码将正确执行。但是,如果getElements()返回null,则会出现空指针异常。为了避免这种情况,您需要添加更多的代码,这会使事情变得更加复杂。返回一个空数组可以让所需的行为在不添加额外代码的情况下自然发生。

是的,这是真的,但正如我在我的例子中提到的那样,这种方法并不总是正确的。 - Avershin Dmitry

1

我有这个想法已经有一段时间了。 暂且不谈NPE问题,我将从面向对象编程的角度来解释我的想法。

假设你编写了一个类,提供了一个Glass实例的getter方法 public Glass getGlass()。 当我读到这个签名时,我期望得到一个Glass接口的实例,而null不是其中之一。

你可以考虑下一个方法 public Optional<Glass> getGlass()

Optional类(可以编写自己的或使用Google's guava Optional)包含您的通用类实例,同时提供isPresent等函数。 它可能看起来多余,因为您可以只使用getGlass() != null而不是从客户端代码中使用getGlass().isPresent()。但这里的主要优点是签名直观明了。没有人隐藏任何东西,你也不必猜测。

所以你可以获得以下好处:

  1. 避免NPE
  2. 直观明了的签名
  3. 干净的客户端代码

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接