当检索方法无法生成返回值时,应该返回“null”还是抛出异常?

551

我正在使用Java语言,我有一个方法,如果找到了对象,就应该返回它。

如果没有找到,我应该:

  1. 返回null
  2. 抛出异常
  3. 其他

哪种是最佳实践或习惯用法?


65
无论你做什么,一定要记录下来。我认为这一点比哪种方法是“最好”的更重要。 - Rik
7
这取决于所使用的编程语言中流行的习语。请在问题上打上相应的编程语言标签。 - Teddy
3
返回 null 可能仅表示成功或失败,这通常并不能提供足够的信息(有些方法可能以多种方式失败)。 库应该更好地抛出异常,使错误明确化,这样主程序可以在更高的层次上决定如何处理错误(相对于内置错误处理逻辑而言)。 - 3k-
5
在我看来,真正的问题是我们是否应该认为找不到实体是例外情况,如果是,为什么?没有人真正回答如何得出这个结论,现在问答关闭了。这个行业还没有在这个重要的话题上达成共识,真是遗憾。是的,我知道这取决于情况。因此,请解释为什么会有这种情况,不只是简单地说“如果是例外情况,就排除掉”。 - crush
36个回答

2
一般来说,它应该返回null。调用该方法的代码应该决定是抛出异常还是尝试其他操作。

2

或者返回一个选项

Option是一个容器类,强制客户端处理两种情况。Scala有这个概念,请查看它的API。

然后你有像T getOrElse(T valueIfNull)这样的方法在此对象上,它要么返回找到的对象,要么是客户端指定的替代方法。


2

很不幸,JDK并不一致,当你试图访问资源包中不存在的键时,会抛出未找到异常,而当你从映射中请求值时,如果该值不存在,则返回null。所以我建议修改优胜答案为:如果找到的值可能为空,则在未找到时引发异常,否则返回null。因此,请遵循这个规则,但有一个例外,如果您需要知道为什么找不到值,那么总是引发异常,或者......


1
只要它应该返回一个对象的引用,返回NULL就可以了。
然而,如果它返回整个东西(比如在C++中,如果你写成'return blah;'而不是'return &blah'(或者'blah'是一个指针),那么你不能返回NULL,因为它不是'type object'。在这种情况下,抛出异常或返回一个没有设置成功标志的空对象是我解决问题的方法。

1

没有人提到异常处理中的开销 - 加载和处理异常需要额外的资源,因此除非这是一个真正的应用程序终止或进程停止事件(继续进行会带来更多的伤害),否则我会选择返回一个值,让调用环境自行解释。


1

我同意这里似乎达成的共识(如果“未找到”是正常可能的结果,则返回null,或者如果情况的语义要求始终找到对象,则抛出异常)。

然而,根据您特定的情况,还有第三种可能性可能是有意义的。您的方法可以在“未找到”条件下返回某种默认对象,从而使调用代码确信它将始终接收到有效对象,而无需进行null检查或异常捕获。


1

返回一个 null,异常就是你的代码执行了一些意料之外的操作。


1

异常应该是特殊情况。如果返回null是有效的,则返回null


1
或多或少是真的。请参见http://www.boost.org/community/error_handling.html。 - Martin Cote

1
如果您使用的是一个抛出异常的库或其他类,您应该重新抛出它。这里有一个例子。Example2.java 就像一个库,而 Example.java 使用它的对象。Main.java 是处理此异常的示例。您应该在调用方向用户显示有意义的消息和(如有必要)堆栈跟踪。

Main.java

public class Main {
public static void main(String[] args) {
    Example example = new Example();

    try {
        Example2 obj = example.doExample();

        if(obj == null){
            System.out.println("Hey object is null!");
        }
    } catch (Exception e) {
        System.out.println("Congratulations, you caught the exception!");
        System.out.println("Here is stack trace:");
        e.printStackTrace();
    }
}
}

Example.java

/**
 * Example.java
 * @author Seval
 * @date 10/22/2014
 */
public class Example {
    /**
     * Returns Example2 object
     * If there is no Example2 object, throws exception
     * 
     * @return obj Example2
     * @throws Exception
     */
    public Example2 doExample() throws Exception {
        try {
            // Get the object
            Example2 obj = new Example2();

            return obj;

        } catch (Exception e) {
            // Log the exception and rethrow
            // Log.logException(e);
            throw e;
        }

    }
}

Example2.java

 /**
 * Example2.java
 * @author Seval
 *
 */
public class Example2 {
    /**
     * Constructor of Example2
     * @throws Exception
     */
    public Example2() throws Exception{
        throw new Exception("Please set the \"obj\"");
    }

}

如果从函数中抛出的异常不是运行时异常,并且调用者需要处理它(而不仅仅是终止程序),那么最好将内部子系统中的异常包装在外部受检异常中,同时“链接”内部异常,以便进行调试。例如,example1可以使用'throw new MyCheckedException("Please set the "obj"", e)'来包含'e'在抛出的异常中。 - Some Guy

0

这真的取决于您是否希望找到该对象。如果您遵循异常应用于指示某些异常情况的思想,则:

  • 找到对象;返回对象
  • 未找到对象;抛出异常

否则,返回 null。


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