使用NullPointerException来测试null是否是一种不好的编程风格吗?

9

我有一些类似以下模式的代码:

return a().b().c().d().e();

现在,由于这些方法中的每一个都可能返回null,因此通常需要进行以下测试:

if( (a()!=null) && (a().b() != null) && ....) {
   return a().b().c().d().e();
} else  {
  return null;
}

(也许可以使用一些本地变量来避免重复调用)

我想这样做:

try {
   return a().b().c().d().e();
} catch (NullPointerException e) {
   return null;
}

这算是不好的编写风格吗?效率低下?还是可以接受的?

我认为使用异常来控制程序流程是不好的。 - Sweeper
6个回答

16

不要这样做。与基本的 null 检查相比,抛出和捕获异常相当昂贵。

您可能还想知道,在 Java 的未来版本中,已经提出了语法来使此操作更简单。它会类似于这样:

a()?.b()?.c()?.d()

"?."操作符将是"."操作符的可选版本。如果LHS为空,它会在那一点上返回null而不是尝试评估RHS。这正是您要寻找的,但我担心它没有被Java 7采纳。不知道Java 8中这个功能的状态。


但是,如果他使用try-catch来做这件事...所以,无论如何,它都会工作,你知道的。为什么我们不应该使用编程语言的特性呢?是的,也许通过空检查来检查它会更快一些,但再次说一遍:无论速度是否重要... - Martijn Courteaux
1
@Martijn Courteaux:问题不在于捕捉NPE是否有效。显然是有效的。问题在于是否养成编写此类代码的习惯是一个好主意。它不是。 - Konstantin Komissarchik

5

通常情况下,这行代码本身是有问题的,忽略了null的问题。详见"迪米特法则"或"最少知识原则"

return a().b().c().d().e();

如果我对a、b、c、d和e没有控制权,我会重新编写以下内容。
if( (a()!=null) && (a().b() != null) && ....) {
   return a().b().c().d().e();
} else  {
  return null;
}

作为这个意思,当某些东西出现问题并且我需要阅读堆栈跟踪时,这是仍然令人厌恶但更加有用的。
B b = a.a();
if(b == null)
{
   return null;
}

C c = b.b();
if(c == null)
{
   return null;
}

D d = c.c();
if(d == null)
{
   return null;
}

return d.d();

不,捕获NullPointerException并不是一个合适的做法。这有点像在for循环中使用try块来捕获ArrayIndexOutOfBoundsException。
如果这应该是一个链式调用是优势和目标的流畅API,那么它永远不应该返回null。

1
我基本上同意,但有时候你没有选择权,例如在使用第三方库时。另一个经常发生这种情况的案例是对象关系数据库映射,例如:resultSet.getFirst().getPerson().getBusinessUnit().getAddress().getPostCode(); 如果数据库模式允许非常部分的信息,这种情况可能会经常发生。 - Carsten
+1 对于数组越界比较,非常准确。 - Epaga

5
这被认为是不好的风格,因为异常应该表示异常情况,即在正常执行过程中不太可能出现的情况,并指示出现了问题。使用异常来检查对象是否为空使用了这种机制来检查更加平凡的失败。正如Konstantin的帖子所提到的,使用异常也会有运行时代价。此外,将所有错误都包装在一个单一的NullPointerException中意味着您失去了关于具体出了什么问题的信息。哪个函数返回了null?这是由于正常错误还是有更严重的问题?
这里还有其他你没有考虑过的选项。与其让a(),b()等返回null以表示错误,请考虑让它们抛出更详细的异常,说明它们不能返回某些内容的原因。如果它们失败是因为网络连接断开,请让它们抛出IOException或类似的异常。如果它们失败是因为您对数组进行了错误的索引,请明确说明。
简而言之,如果您想使用异常,请使用它们来更精确地通知程序的其余部分发生了什么。这样,您可以采取更细粒度的纠正措施。

2

我不会这样做,主要是因为如果a()b()c()等出现了导致它们抛出NullPointerException的错误,那么你就会捕获并继续执行,而实际上你不应该这样做。


2

更改方法,使其不返回null或避免链接可能返回null的方法。


1

不要返回 null,让它们每个都返回 NullObject 或其他东西。了解Null Object Pattern


1
虽然空对象可能很有用,但它可能会隐藏一些问题并使调试变得更加困难。如果唯一的收益是在一个地方节省几个测试,请不要使用它。 - maaartinus

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