6

如果我有下面的功能,有两种选择

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!");
}

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

我的选择是:

  1. 返回 null 并且应用程序将在某些极端情况下获得 NullPointerException
  2. 返回 null 并使用 (myObject != null) 检查包装该方法的使用
  3. 抛出一个异常,这将在运行时将其炸毁

我想我要问的是,这是抛出异常的正确位置吗?也就是说,如果它进入这种情况,我无能为力。这是归类为“异常”还是我应该检查我的方法返回的内容(这使我的代码看起来很糟糕)。如果我知道它不应该返回 null 那么我应该抛出异常对吗?

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

4

10 回答 10

10

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

如果这是一个特殊情况,那么是的。如果预期可能找不到任何符合条件的东西,那么情况并不例外,您应该返回null

于 2012-06-03T20:00:40.700 回答
4

是的,您应该抛出 aRuntimeException来表示不应该发生的“异常”情况。IllegalStateException可能符合要求。确保包含一条消息,其中包含任何可以帮助您找到错误(如果它被抛出)的信息。

于 2012-06-03T19:59:36.567 回答
4

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

  1. null在这个方法返回一个意外的值(即)之后,让你的程序在某个时候崩溃是个好主意吗?
  2. 如果屏蔽null返回值,究竟会隐藏什么?
  3. 仅仅因为值错误就立即炸毁是个好主意吗?

就我个人而言,我会选择选项 2 或 3,这取决于我是否更喜欢问题 2 或 3 的答案。选项 1 绝对是一个坏主意,尤其是在它不应该发生的情况下。如果程序在您的函数返回后抛出 NPE 方式,您将很难弄清楚它的null来源。特别是如果它发生在您完成此特定功能的几个月后。

如果你选择抛出异常,你会立即看到哪里出错了,你可以直接去那里找出出错的原因。在调用函数中返回null并检查它也可以工作,但前提是你不会默默地失败,而是实际做一些事情来正确处理问题。

于 2012-06-03T20:18:02.530 回答
1

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

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

文档有一些示例和说明。和你有类似的情况,由设计模式解决。

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();
  }
}
于 2016-10-06T06:15:37.470 回答
1

除了大多数答案之外,我想指出,如果您关心性能,那么异常比返回 null 慢得多

看看这段代码:

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

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

您将不得不权衡性能与可维护性/可读性。

在此处阅读有关它的更多信息

于 2017-02-09T07:12:42.947 回答
1

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

查看您的第一个代码,外部代码有两种情况得到 NULLPointerException: 1. 碎片为空 2. 碎片没有这样的元素

所以 return null 会误导外部代码进行进一步的操作,它会引起潜在的问题。

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

于 2017-07-21T18:58:56.410 回答
1

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

  1. 调用代码不确定对象是否存在,并且将有代码专门处理它不存在的情况。

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

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

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 throw 代替 return null 以确保不返回 null,因此您不需要任何代码来在调用站点测试 null。

我写了一篇关于这个主题的博文: 返回 Null 还是 Exception? 更详细地介绍了它。

于 2019-05-11T20:09:30.790 回答
0

应该总是返回一个对象返回 null
并且
应用程序会NullPointerException在某些极端情况下
得到一个这两者是矛盾的。

如果你真的确定你总是有pieces[i].isBlank()然后扔IllegalStateException

否则按您的要求处理。

于 2012-06-03T20:01:48.890 回答
0

如果您的数组应始终具有要返回的有效值,则应引发异常作为后备。(在您的示例中为 2sd 案例)

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

于 2012-06-03T20:04:08.450 回答
0

我建议使用我刚才在另一个答案中谈到的Maybe(也称为)数据类型。Option

此数据类型在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 也已经具备了这一点

让我们暂时假设它pieces是一个fj.data.List. 然后你的方法可以重写为:

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

另一个旁注:

也许上面的代码看起来很粗糙。IntelliJ IDEA 的“闭包折叠”在这里可以提供一些帮助

于 2012-06-03T20:25:45.697 回答