在java中尝试/ catch vs null检查

时间:2012-03-18 12:53:49

标签: java

有时我会面对我必须写一段这样的代码(通常它有更多的嵌套if和更复杂的结构,但是对于这个例子来说)

public void printIt(Object1 a){
  if (a!=null){
     SubObject b= a.getB();
     if (b!=null){
         SubObject2 c=b.getC();
         if(c!=null){
             c.print();
         }
     }
  }
}

当我不需要知道什么是失败的,如果什么是空的什么都不做,一种方法可能是

public void printIt(Object1 a){
    try{
      a.getB().getC().print();
    }catch (NullPointerException e) {
    }
}

第二种形式是否存在问题,如表现或其他类型的问题?

10 个答案:

答案 0 :(得分:20)

异常版本(类似于使用Groovy的安全导航操作符?.的链)使得很容易采用得墨忒耳定律(或者我称之为Demeter的强烈建议)并将其作为玩具过夜。

同样,深层嵌套的if - 语句导致难以阅读的代码,并且在其下面存在相同的“违规”,并且此类方法的圈复杂度很高。

public void printIt(Object1 a) {
    if (null == a) {
        return;
    }

    SubObject b = a.getB();
    if (null == b) {
        return;
    }

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

    c.print();
}

我宁愿在适当的地方看到LAM(小辅助方法)封装检查,几乎完全消除了对问题的需求。

答案 1 :(得分:10)

是。第二个版本会有糟糕的表现。

不要将异常用于正常控制流程。有效的Java项目57:仅在特殊情况下使用例外。

== UPDATE ==

甚至忽略了性能问题(根据我的benchmark,异常比以前更快,但不如简单的检查那么快),使用标准程序流的异常就像代码一样臭这个。即使在if语句中,JVM字节码也可以对空检查进行特殊优化。第一个代码示例是非常受欢迎的。

答案 2 :(得分:8)

public void printIt(Object1 a){
    if(a==null){
        throw new IllegalArgumentException("a was null, but this is not allowed here."),
    }
    [...]

快速失败并努力失败。如果shoud不为null,则抛出异常。这将使您的代码更加稳定和可靠。

因此,如果我必须在你的a)和你的b)之间做出决定,我会选择a)。但如果a不能为null,则会隐藏错误情况。

答案 3 :(得分:7)

第二个版本的最大部分是,当NPE发生在getB()内,getC()时,它会被默默地忽略。如前所述,例外情况适用于特殊情况。

答案 4 :(得分:2)

使用异常在性能方面总是一个坏主意,无论机制过去和现在的速度有多慢。每当抛出异常时,将展开完整堆栈以创建堆栈跟踪。因此,就像Lois Wasserman所说的那样,你不应该依赖它们来进行(常规)程序流程,而是针对特殊情况。

嵌套ifs不是美的定义,但会让你能够打印其他信息,比如'B is null'等。

答案 5 :(得分:2)

答案是使用版本A.

使用Exceptions进行流量控制通常被认为是“糟糕的设计”。例外情况是“特殊”,尤其是使用空检查完全可以避免的NPE。此外,使用空值检查,您可以告诉(即记录)哪个术语为空(您不会知道null与版本B的位置)。

请注意,性能 不再是抛出异常的问题(例如,只有在使用堆栈跟踪时才构建堆栈跟踪)。这是一个干净的代码问题。

但是,在某些情况下,使用流控制的异常是不可避免的,例如从SimpleDateFormat.parse()抛出的异常,因为在调用之前没有合理的方法来判断您的输入是不可解析的

答案 6 :(得分:2)

肯定是(a)但是你应该重新构造方法以避免嵌套前面答案中提到的if语句。异常不是它们曾经的性能损失,但仍然 比检查null慢,并且永远不应该像这样用于程序流控制。如果对象可以为null,则应检查它,但如果不允许,则应在分配对象引用的位置快速失败。在许多情况下,您可以使用默认实现(空列表是一个很好的示例)以完全避免空值,从而产生更清晰的代码。尽可能避免使用空值。

答案 7 :(得分:1)

代码永远不应包含未经检查的异常的异常处理程序。对于有可能为null的对象引用,应始终使用空检查。

答案 8 :(得分:0)

如果迁移到Java 8,则可以使用Optional和Lambdas。首先,您需要重写类以返回每种类型的Optional

class Object1 {
    private SubObject b;

    Optional<SubObject> getB() {
        return Optional.ofNullable(b);
    }
}

class SubObject {
    private SubObject2 c;

    Optional<SubObject2> getC() {
        return Optional.ofNullable(c);
    }
}

class SubObject2 {
    @Override
    public String toString() {
        return "to be printed";
    }
}

现在,您可以以简洁的方式链接调用而不会有NullPointerExceptions的风险:

a.getB()
    .flatMap(SubObject::getC)
    .ifPresent(System.out::println);

有关详细信息,请参阅Oracle的文章Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional!

答案 9 :(得分:0)

使用Java 8可选:

Optional.ofNullable(a)
            .map(Object1::getB)
            .map(SubObject::getC)
            .ifPresent(Object2::print);