抛出异常或返回null

时间:2012-06-03 19:57:32

标签: java exception coding-style null

如果我有以下功能,有两个选择

private MyObject findBlank() {
    for (int i = 0; i < pieces.length; i++) {
        if(pieces[i].isBlank()){
            return pieces[i];
        }
    }
    return null;
}

private MyObject findBlank() {
    for (int i = 0; i < pieces.length; i++) {
        if(pieces[i].isBlank()){
            return pieces[i];
        }
    }
    throw new NoSuchFieldError("No blank piece found!");
}

从这个方法我知道它应该总是返回一个对象其中一个'pieces'始终是isBlank() == true,最后的返回null只是为了取悦编译器。既然是这种情况,如果它返回null我的代码无论如何都不会工作,这是正确的请抛出异常吗?

我的选择是:

  1. 返回null,应用程序将在某些边缘情况下获得NullPointerException
  2. 返回null并使用(myObject!= null)检查
  3. 包装方法
  4. 抛出一个会在运行时将其炸毁的异常
  5. 我想我要问的是,这是抛出异常的正确位置吗?也就是说,如果遇到这种情况,我无能为力。这被归类为'例外'还是我应该检查我的方法返回什么(这使我的代码看起来很糟糕)。如果我知道它不应该返回null那么我应该抛出异常吗?

    另外,我如何选择什么异常,或者扩展一个并抛出我自己的异常呢?

10 个答案:

答案 0 :(得分:10)

  

我想我要问的是,这是抛出异常的正确位置吗?

如果是例外情况,那么是。如果找不到符合条件的任何内容的可能性预期那么情况不是例外,您应该返回null

答案 1 :(得分:4)

是的,您应该抛出RuntimeException来表示不应发生的“例外”情况。 IllegalStateException可能符合要求。确保包含一条消息,其中包含任何可以帮助您找到错误的信息。

答案 2 :(得分:4)

关于您的选择,问自己是否

  1. 在此方法返回意外值(即null)之后,让您的程序在某个时刻爆炸是个好主意吗?
  2. 如果屏蔽null返回值,究竟会隐藏什么?
  3. 立即爆炸是个好主意,只是因为错误值?
  4. 我个人会选择2或3,这取决于我是否更喜欢问题2或3的答案。选项1肯定是一个坏主意,特别是如果它不应该发生。如果程序在函数返回后抛出NPE方式,那么您将很难确定null来自何处。特别是如果它在你完成这个特定功能的几个月后发生。

    如果您选择抛出异常,您会立即看到 出现问题的地方,您可以直接找到为什么出错了。返回null并在调用函数中检查它也可以工作,但前提是你没有以静默方式失败,而是实际做了一些事情来正确处理问题。

答案 3 :(得分:1)

使用Null Object pattern可能是个好主意。

Provide an object as a surrogate for the lack of an object of a given type. The Null Object provides intelligent do nothing behavior, hiding the details from its collaborators

因此,在这种情况下,您不必使用异常或返回null。您始终可以返回预期的返回类型对象。诀窍是当你没有任何东西可以返回时,你可以返回与预期返回类型相同类型的null,而不是返回Null object或抛出异常。

documentation有一些示例和说明。并且有类似的情况,通过设计模式解决。

public class CustomerFactory {

  public static final String[] names = {"Rob", "Joe", "Julie"};

  public static AbstractCustomer getCustomer(String name){   
    for (int i = 0; i < names.length; i++) {
       if (names[i].equalsIgnoreCase(name)){
         return new RealCustomer(name);
       }
    }
    return new NullCustomer();
  }
}

答案 4 :(得分:1)

除了大多数答案之外,我想指出,如果表现是您关注的问题,例外情况要慢于返回空白

看看这段代码:

class Main {
    public static void main(String[] args) {
        testException();
        testNull();
    }

    public static void testException() {
        long st = System.currentTimeMillis();
        for(int i=0;i<10000000;i++) {
            try{
                exp();
            } catch(Exception e) {

            }
        }
        long et = System.currentTimeMillis();
        System.out.println("Time taken with exceptions : "+(et-st));
    }

    public static void testNull() {
        long st = System.currentTimeMillis();
        for(int i=0;i<10000000;i++) {
            returnNull();
        }
        long et = System.currentTimeMillis();
        System.out.println("Time taken with null : "+(et-st));
    }

    public static void exp() throws Exception {
        throw new Exception();
    }

    public static Object returnNull() {
        return null;
    }
}

我机器上的结果是:

Time taken with exceptions : 7526
Time taken with exceptions : 5

如果抛出异常是代码中的罕见情况并且不会经常发生,那么在这两种情况下所花费的时间几乎相同。

您必须权衡效果与可维护性/可读性

详细了解here

答案 5 :(得分:1)

返回null大部分时间都会绘制从合同视图中丢失的信息,如果从生产者那里获取null,则消费者无法知道错误响应的原因是什么。

查看你的第一个代码,外部代码有两种情况得到NULLPointerException: 这些碎片是空的 这些碎片没有这样的元素

因此,返回null将导致外部代码误导以进一步操作,它将引发潜在的问题。

并讨论返回nullObject(非null)和异常之间的区别,主要区别是PROBABILITY,这意味着: 1.如果空情概率更高,它应该返回nullObject,以便所有外部代码可以/应该显式处理它们。 2.如果空情况的概率较小,为什么不抛出异常,以便最终调用函数可以直接处理它。

答案 6 :(得分:1)

我相信正确的答案取决于调用该方法的代码。有两种情况:

  1. 调用代码不确定对象是否存在,并且将具有处理特殊情况下不存在的代码。

  2. 调用代码确信该对象存在,并且如果不存在该对象,则逻辑上还会存在其他深层问题,在这种情况下,您所能做的就是放弃并报告错误。 / p>

在实践中,我经常使用命名约定来实现两者,以区分它们:

private MyObject findBlankOrNull() {
    for (int i = 0; i < pieces.length; i++) {
        if(pieces[i].isBlank()){
            return pieces[i];
        }
    }
    return null;
}

private MyObject findBlankOrFail() throws Exception {
    MyObject obj = findBlankOrNull();
    if (obj != null) {
        return obj;
    }
    throw new NoSuchFieldError("No blank piece found!");
}

请注意,始终可以构造OnFail版本来调用另一个版本,然后引发异常而不是返回null。本质上,您正在做的是插入Exception异常来代替return null,以确保不返回null,因此您不需要任何代码就可以在调用站点测试null。

我写了一篇有关该主题的博客文章:Return Null or Exception?,其中有更详细的介绍。

答案 7 :(得分:0)

应始终返回一个对象返回null

该应用会在某些边缘情况下获得NullPointerException 这两者相互矛盾。

如果您确定自己已经pieces[i].isBlank(),那么请IllegalStateException

否则请根据您的要求处理案件。

答案 8 :(得分:0)

如果数组应始终具有要返回的有效值,则应将异常作为回退引发。 (在你的例子中为2sd)

最终,您可以声明自己的异常类(Class)。

答案 9 :(得分:0)

我建议您使用我刚刚在another answer中谈到的Maybe(也称为Option)数据类型。

is available中的此数据类型Functional Java

<强>用法:

private Option<MyObject> findBlank() {
    for (int i = 0; i < pieces.length; i++) {
        if(pieces[i].isBlank()){
            return Option.some(pieces[i]);
        }
    }
    return Option.none();
}

<强>旁注:

您的findBack方法可以推广到一个将谓词作为参数的方法,并查找并返回满足它的第一个元素。

不出所料,Functional Java already has that as well

我们暂时假设piecesfj.data.List。然后您的方法可以重写为:

private Option<MyObject> findBlank() {
  return pieces.find(new F1<MyObject, Boolean>() {
    public Boolean f(MyObject p) {
      return p.isBlank();
    }
  });
}

另一个旁注:

也许上面的代码看起来很粗糙。 IntelliJ IDEA's "closure folding" can be of some help here