检索方法是否应返回'null'或在无法产生返回值时抛出异常?

时间:2008-10-06 18:19:28

标签: exception error-handling null

我有一个方法,如果找到它,它应该返回一个对象。

如果找不到,我应该:

  1. return null
  2. 抛出异常
  3. 其他

36 个答案:

答案 0 :(得分:455)

如果您总是希望找到一个值,那么抛出异常(如果缺少)。例外意味着存在问题。

如果值可能丢失或存在且两者都对应用程序逻辑有效,则返回null。

更重要的是:你在代码中的其他地方做了什么?一致性很重要。

答案 1 :(得分:95)

如果确实是错误,则仅抛出异常。如果对象的预期行为不存在,则返回null。

否则这是一个偏好的问题。

答案 2 :(得分:64)

作为一般规则,如果方法应始终返回一个对象,那么请使用异常。如果您预计偶尔会出现空值并希望以某种方式处理它,请使用null。

无论你做什么,我都强烈反对第三种选择:返回一个写着“WTF”的字符串。

答案 3 :(得分:50)

如果null从不表示错误,则只返回null。

如果null始终为错误,则抛出异常。

如果null有时是异常,那么编写两个例程。一个例程抛出异常,另一个是布尔测试例程,它返回输出参数中的对象,如果找不到对象,例程返回false。

很难滥用Try例程。忘记检查空值真的很容易。

所以当null是一个错误你只需写

object o = FindObject();

当null不是错误时,您可以编写类似

的代码
if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found

答案 4 :(得分:25)

我只是想概括之前提到的选项,抛出一些新的选项:

  1. return null
  2. 抛出异常
  3. 使用空对象模式
  4. 为您提供一个布尔参数方法,因此调用者可以选择是否要他抛出异常
  5. 提供一个额外的参数,因此如果没有找到值,调用者可以设置一个他回来的值
  6. 或者您可以将这些选项组合在一起:

    提供几个重载版本的getter,以便调用者可以决定走哪条路。在大多数情况下,只有第一个具有搜索算法的实现,而其他的只是围绕第一个:

    Object findObjectOrNull(String key);
    Object findObjectOrThrow(String key) throws SomeException;
    Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
    Object findObjectOrDefault(String key, Object defaultReturnValue);
    

    即使您选择只提供一个实现,您也可能希望使用这样的命名约定来阐明您的合同,如果您决定添加其他实现,它也会有所帮助。

    你不应该过度使用它,但它可能会有所帮助,特别是在编写一个帮助程序类时,你将在数百种不同的应用程序中使用它们,它们具有许多不同的错误处理约定。

答案 5 :(得分:18)

使用null对象模式或抛出异常。

答案 6 :(得分:13)

与您正在使用的API保持一致。

答案 7 :(得分:11)

只要问问自己:“是不是找不到物体的特殊情况”?如果预计会在程序的正常过程中发生,则可能不应该引发异常(因为它不是异常行为)。

简短版本:使用异常来处理异常行为,而不是处理程序中的正常控制流。

-Alan。

答案 8 :(得分:11)

这取决于您的语言和代码是否促进: LBYL(在你跳跃之前看) 要么 EAFP(更容易请求宽恕而不是许可)

LBYL说您应该检查值(因此返回null)
EAFP表示只是尝试操作并查看它是否失败(抛出异常)

虽然我同意上述..异常应该用于异常/错误条件,并且在使用检查时返回null是最好的。


EAFP与Python中的LBYL:
http://mail.python.org/pipermail/python-list/2003-May/205182.htmlWeb Archive

答案 9 :(得分:8)

抛出异常的优点:

  1. 调用代码中的清洁控制流。检查null会注入一个由try / catch本机处理的条件分支。检查null并不表示你正在检查它是什么 - 你是否正在检查null是因为你正在寻找你期望的错误,或者你是否正在检查null因此你没有在downchain上进一步传递它?
  2. 消除“null”的含义模糊。 null是否代表错误或为null实际存储在值中的是什么?很难说什么时候只有一件事情可以作出决定。
  3. 改进了应用程序中方法行为之间的一致性。异常通常在方法签名中公开,因此您更能够了解应用程序中的方法占用的边缘情况,以及您的信息应用程序可以以可预测的方式做出反应。
  4. 有关示例的更多说明,请参阅:http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

答案 10 :(得分:5)

例外与合同设计有关。

对象的接口实际上是两个对象之间的契约,调用者必须满足契约,否则接收者可能会因异常而失败。有两种可能的合同

1)所有输入方法都有效,在这种情况下,你必须在找不到对象时返回null。

2)只有一些输入有效,即导致找到对象的输入。在这种情况下,您必须提供第二种方法,允许调用者确定其输入是否正确。例如

is_present(key)
find(key) throws Exception

IF并且只有在你提供第二份合同的两种方法的情况下,你才可以抛出异常,但没有找到任何东西!

答案 11 :(得分:4)

取决于找不到该对象的含义。

如果这是正常的事态,则返回null。这只是偶尔会发生的事情,呼叫者应该检查它。

如果是错误,则抛出异常,调用者应该决定如何处理丢失对象的错误情况。

最终要么会工作,尽管大多数人通常认为只有在出现异常时才使用例外是一种好习惯。

答案 12 :(得分:4)

我更喜欢只返回一个null,并依赖调用者来适当地处理它。 (因为缺少一个更好的词)例外是如果我绝对'确定'这个方法将返回一个对象。在这种情况下,失败是一种特殊的应该而且应该抛出。

答案 13 :(得分:4)

以下是一些建议。

如果返回一个集合,请避免返回null,返回一个空集合,这使得枚举更容易处理而不先进行空检查。

几个.NET API使用thrownOnError参数的模式,该参数为调用者提供了选择,如果找不到该对象,它是否真的是异常情况。 Type.GetType就是一个例子。 BCL的另一种常见模式是TryGet模式,其中返回一个布尔值,并通过输出参数传递该值。

在某些情况下,您也可以考虑Null对象模式,可以是默认模式,也可以是没有行为的版本。关键是避免在整个代码库中进行空检查。有关详细信息,请参阅此处http://geekswithblogs.net/dsellers/archive/2006/09/08/90656.aspx

答案 14 :(得分:3)

仅指null不被视为异常行为的情况我绝对是try方法,很明显,不需要“读书”或“在你跳跃之前看”,如此处所述

基本上是这样的:

bool TryFindObject(RequestParam request, out ResponseParam response)

这意味着用户的代码也将清晰

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...

答案 15 :(得分:3)

返回null而不是抛出异常,并清楚地记录API文档中返回值为null的可能性。如果调用代码不遵守API并检查null情况,那么它很可能会导致某种“空指针异常”:)

在C ++中,我可以想到设置查找对象的方法的3种不同风格。

选项A

Object *findObject(Key &key);

无法找到对象时返回null。很好,很简单。我会选择这个。下面的替代方法适用于那些不讨厌外语的人。

选项B

void findObject(Key &key, Object &found);

传入对将接收对象的变量的引用。当无法找到对象时,该方法抛出异常。如果不能找到一个没有找到的对象,那么这个约定可能更合适 - 因此你抛出一个异常来表示它是一个意想不到的情况。

选项C

bool findObject(Key &key, Object &found);

无法找到对象时,该方法返回false。这个优于选项A的优点是您可以通过一个明确的步骤检查错误情况:

if (!findObject(myKey, myObj)) { ...

答案 16 :(得分:3)

在某些功能中,我添加了一个参数:

..., bool verify = true)

True表示throw,false表示返回一些错误返回值。这样,使用此功能的人都有两种选择。默认值应为true,以免忘记错误处理的人。

答案 17 :(得分:2)

不幸的是,JDK是不一致的,如果您尝试访问资源包中的非现有密钥,则不会发现异常,并且当您从map请求值时,如果它不存在则返回null。所以如果找到的值可以为null,那么我会将获胜者的答案更改为以下内容,然后在找不到时引发异常,否则返回null。所以遵循规则有一个例外,如果你需要知道为什么找不到值然后总是引发异常,或者..

答案 18 :(得分:2)

通常它应该返回null。调用该方法的代码应该决定是抛出异常还是尝试别的东西。

答案 19 :(得分:2)

或返回选项

选项基本上是一个容器类,它强制客户端处理booth案例。 Scala有这个概念,查找它的API。

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

答案 20 :(得分:2)

如果客户端代码知道找到和未找到之间的区别很重要,这应该是常规行为,那么最好返回null。然后,客户端代码可以决定要做什么。

答案 21 :(得分:1)

如果方法返回一个集合,则返回一个空集合(如上所述)。但请不要Collections.EMPTY_LIST等! (如果是Java)

如果方法检索单个对象,那么您有一些选项。

  1. 如果该方法总是应该找到结果并且这是一个真正的例外情况而不是找不到对象,那么你应该抛出一个异常(在Java中:请一个未选中的异常)
  2. (仅限Java)如果您可以容忍该方法抛出已检查的异常,则抛出项目特定的ObjectNotFoundException等。在这种情况下,如果您忘记处理异常,编译器会告诉您。 (这是我首选处理Java中未找到的东西。)
  3. 如果您说它确实没问题,如果找不到该对象并且您的方法名称类似于findBookForAuthorOrReturnNull(..),那么您可以返回null。在这种情况下,强烈建议使用某种静态检查或编译器检查,这样可以防止在没有空检查的情况下取消引用结果。在Java的情况下,它可以是例如。 FindBugs(参见http://findbugs.sourceforge.net/manual/annotations.html的DefaultAnnotation)或IntelliJ-Checking。
  4. 如果您决定返回null,请注意。如果您不是项目中唯一的程序员,您将在运行时获得NullPointerExceptions(使用Java或其他语言中的任何语言)!因此,不要返回在编译时未检查的空值。

答案 22 :(得分:1)

首选返回null -

如果调用者在没有检查的情况下使用它,则无论如何都会发生异常。

如果来电者没有真正使用它,请不要向他征税try / catch阻止

答案 23 :(得分:1)

例外情况应为例外。如果返回null 有效,则返回null

答案 24 :(得分: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\"");
    }

}

答案 25 :(得分:1)

返回一个null,异常就是这样:你的代码所做的事情是不期望的。

答案 26 :(得分:1)

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

然而,根据您的具体情况,第三种可能性是有意义的。您的方法可以在“未找到”条件下返回某种类型的默认对象,从而允许调用代码确保它始终接收有效对象而无需空检查或异常捕获。

答案 27 :(得分:1)

不要认为有人提到异常处理的开销 - 需要额外的资源来加载和处理异常,所以除非它真正的应用程序查杀或进程停止事件(前进会造成更多弊大于利)我会选择传回一个值,调用环境可以根据需要进行解释。

答案 28 :(得分:1)

只要它应该将引用返回给对象,返回NULL应该是好的。

然而,如果它正在返回整个血腥的东西(比如在C ++中,如果你这样做:'返回blah;'而不是'return&amp; blah;'(或'blah'是指针),那么你就无法返回一个NULL,因为它不是'object'类型。在这种情况下,抛出一个异常,或者返回一个没有设置成功标志的空白对象就是我如何处理这个问题。

答案 29 :(得分:0)

在正常操作期间不包含该对象,并且应该由调用者处理返回NULL。

如果不包含该对象通过调用代码或内部状态指示错误,则执行断言。

如果不包含该对象则表示不常发生的事件。 (就像有人在你同时检查项目时从商店中删除了一个项目。)然后抛出异常。

答案 30 :(得分:0)

在数据层代码中,我有时会使用以下代码,允许调用者判断“找不到对象”是否意味着发生了错误。


DataType GetObject(DBConnection conn, string id, bool throwOnNotFound) {
    DataType retval = ... // find object in database
    if (retval != null || ! throwOnNotFound) {
        return retval;
    } else {
        throw new NoRowsFoundException("DataType object with id {id} not found in database");
    }
}

DataType GetObject(DBConnection conn, string id) {
    return GetObject(conn, id, true);
} 

答案 31 :(得分:0)

“other”选项可以让find方法使用一个默认对象的附加参数,如果找不到找到的对象,将返回该参数。

否则我只返回null,除非在找不到该对象时它确实是一种例外情况。

答案 32 :(得分:0)

这取决于你的方法。如果你的方法应该总是返回一个有效的对象而没有找到,那么抛出异常是可行的方法。如果该方法仅用于返回可能存在或可能不存在的对象(例如,可能是联系人上的图像),则不应引发错误。

如果此方法实际上将返回一个对象,那么您可能还希望公开一个返回布尔值true / false的方法,这样您就不必a)检查null或b)捕获异常

答案 33 :(得分:0)

如果没有发现它是特殊事件(即在正常情况下它应该在那里),那么扔掉。否则,返回“未找到”值(可以为null,但不必),或者甚至让方法返回找到/未找到的布尔值和实际对象的out参数。

答案 34 :(得分:0)

这取决于方法的性质以及如何使用它。如果找不到该对象是正常行为,则返回null。如果始终找到对象是正常行为,则抛出异常。

根据经验,仅在发生异常时使用例外。不要以异常抛出和捕获是其正常操作的一部分的方式编写代码。

答案 35 :(得分:0)

这实际上取决于您是否希望找到该对象。如果你遵循思想学派,应该使用例外来表示某些东西,那么,错误,异常已经发生了:

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

否则,返回null。