531

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

如果没有找到,我应该:

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

哪个是最佳实践或成语?

4

36 回答 36

509

如果您总是期望找到一个值,那么如果它丢失则抛出异常。异常意味着存在问题。

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

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

于 2008-10-06T18:21:50.410 回答
104

只有当它确实是错误时才抛出异常。如果对象的预期行为不存在,则返回 null。

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

于 2008-10-06T18:21:36.277 回答
77

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

无论你做什么,我强烈建议不要使用第三种选择:返回一个显示“WTF”的字符串。

于 2008-10-06T18:22:30.300 回答
52

如果 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
于 2008-10-06T18:52:05.967 回答
27

我只是想概括一下前面提到的选项,并添加一些新选项:

  1. 返回空
  2. 抛出异常
  3. 使用空对象模式
  4. 为您的方法提供一个布尔参数,因此调用者可以选择是否希望您抛出异常
  5. 提供一个额外的参数,所以调用者可以设置一个值,如果没有找到值,他会返回

或者您可以结合这些选项:

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

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

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

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

于 2009-01-04T18:00:52.787 回答
19

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

于 2008-10-06T18:21:04.687 回答
15

抛出异常的优点:

  1. 调用代码中的更清晰的控制流。 检查 null 会注入一个由 try/catch 本地处理的条件分支。检查 null 并不表示您正在检查什么 - 您检查 null 是因为您正在寻找您期望的错误,还是检查 null 以便您不会在下行链上进一步传递?
  2. 消除“null”含义的歧义。 null 是代表错误还是 null 是实际存储在值中的内容?很难说当你只有一件事可以做出这种决定时。
  3. 改进了应用程序中方法行为之间的一致性。 异常通常暴露在方法签名中,因此您更能够了解应用程序中的方法处理哪些边缘情况,以及您的应用程序可以以可预测的方式对哪些信息做出反应。

有关示例的更多解释,请参阅: http: //metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/

于 2011-11-17T05:35:54.863 回答
13

与您使用的 API 保持一致。

于 2008-10-06T18:20:53.503 回答
13

这取决于您的语言和代码是否提倡:LBYL(在您跳跃之前查看)或 EAFP(更容易请求宽恕而不是许可)

LBYL 说你应该检查值(所以返回 null)
EAFP 说只是尝试操作,看看它是否失败(抛出异常)

尽管我同意上述观点。异常应该用于异常/错误条件,并且在使用检查时最好返回 null。


EAFP 与 Python 中的 LBYL:http://mail.python.org/pipermail/python-list/2003-May/205182.html
网络存档

于 2008-10-06T19:22:00.070 回答
13

问问自己:“找不到对象是例外情况吗”?如果预计它会在您的程序的正常过程中发生,您可能不应该引发异常(因为它不是异常行为)。

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

-艾伦。

于 2008-10-06T22:16:57.550 回答
6

例外情况与按合同设计有关。

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

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

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

is_present(key)
find(key) throws Exception

IF且仅当您提供第二份合同的两种方法时,您才可以抛出异常,即什么都没有!

于 2008-10-06T23:37:56.747 回答
4

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

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

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

最终,两者都行得通,尽管大多数人通常认为仅在发生某些事情时才使用异常是一种好习惯,嗯,异常发生了。

于 2008-10-06T18:22:42.657 回答
4

这里还有一些建议。

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

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

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

于 2008-10-07T00:07:22.977 回答
3

我更喜欢只返回一个空值,并依靠调用者来适当地处理它。(因为没有更好的词)例外是如果我绝对“确定”这个方法将返回一个对象。在这种情况下,失败是一个异常应该并且应该抛出的异常。

于 2008-10-06T18:20:32.160 回答
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)) { ...
于 2008-10-06T18:28:38.680 回答
3

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

..., bool verify = true)

true 表示抛出,false 表示返回一些错误返回值。这样,无论谁使用此功能,都有两种选择。默认值应该是 true,以便那些忘记错误处理的人受益。

于 2008-10-06T20:00:19.730 回答
3

仅指 null 不被视为异常行为的情况,我绝对是 try 方法,很明显,不需要像这里所说的“阅读本书”或“在你跳跃之前看看”

所以基本上:

bool TryFindObject(RequestParam request, out ResponseParam response)

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

...
if(TryFindObject(request, out response)
{
  handleSuccess(response)
}
else
{
  handleFailure()
}
...
于 2010-12-22T16:25:45.543 回答
2

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

于 2008-10-06T18:21:21.347 回答
2

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

于 2008-10-06T18:21:25.060 回答
2

或返回一个选项

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

然后,您在此对象上有 T getOrElse(T valueIfNull) 之类的方法,它们要么返回找到的对象,要么返回客户端指定的替代方案。

于 2008-10-06T19:04:45.147 回答
2

更喜欢返回 null --

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

如果调用者并没有真正使用它,请不要向他征税try/catch

于 2011-02-23T06:02:23.633 回答
2

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

于 2011-08-03T00:32:52.190 回答
1

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

但是,如果它返回整个该死的东西(就像在 C++ 中,如果你这样做:'return blah;' 而不是 'return &blah;' (或 'blah' 是一个指针),那么你不能返回一个 NULL,因为它是不是“对象”类型。在这种情况下,抛出异常或返回没有设置成功标志的空白对象是我解决问题的方法。

于 2008-10-06T18:23:26.227 回答
1

不要认为有人提到异常处理中的开销 - 需要额外的资源来加载和处理异常,所以除非它是一个真正的应用程序终止或进程停止事件(前进会弊大于利)我会选择传回value 调用环境可以按照它认为合适的方式解释。

于 2008-10-06T18:29:42.407 回答
1

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

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

于 2008-10-06T18:36:04.890 回答
1

返回一个空值,异常就是这样:你的代码做的事情不是预期的。

于 2008-10-06T19:09:04.933 回答
1

例外应该是例外。如果返回 null有效,则返回 null

于 2008-10-06T22:26:07.250 回答
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。

如果您决定返回空值,请小心。如果您不是项目中唯一的程序员,您将在运行时获得 NullPointerExceptions(在 Java 或其他语言中)!所以不要返回在编译时未检查的空值。

于 2012-08-21T13:13:22.377 回答
1

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

主.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\"");
    }

}
于 2014-10-22T12:27:19.677 回答
0

这实际上取决于您是否希望找到该对象。如果您遵循应该使用异常来指示某事的思想流派,那么,错误,异常已经发生:

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

否则,返回空值。

于 2008-10-06T18:21:33.657 回答
0

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

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

于 2008-10-06T18:22:48.803 回答
0

如果没有发现是异常事件(即正常情况下应该存在),则抛出。否则,返回一个“未找到”值(可以为 null,但不是必须),或者甚至让该方法返回一个布尔值表示 found/notfound 和一个实际对象的 out 参数。

于 2008-10-06T18:23:31.250 回答
0

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

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

于 2008-10-06T18:23:31.483 回答
0

“其他”选项可以是让 find 方法采用一个带有默认对象的附加参数,如果找不到所寻找的对象,该参数将被返回。

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

于 2008-10-06T18:25:20.170 回答
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);
} 
于 2008-10-06T18:42:29.293 回答
0

它不包含对象可能发生在正常操作期间,应由调用者处理返回NULL。

如果不包含该对象表明调用代码或内部状态存在错误,则执行断言。

如果不包含该对象,则表示偶发事件。(就像有人在您同时签出商品时从商店中删除了商品一样。)然后抛出异常。

于 2008-10-06T20:07:38.217 回答