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

Rob*_*ert 490 error-handling null exception

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

如果找不到,我应该:

  1. 返回null
  2. 抛出一个例外
  3. 其他

Ken*_*Ken 472

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

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

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

  • @Ken:+ 1,如果你确实抛出一个异常,但可以先验地检测它(比如HasItem(...)),那么用户应该提供所说的Has*或Contains方法. (29认同)
  • 当值丢失时,不要返回值或null,请考虑返回Maybe <T>.请参阅http://mikehadlow.blogspot.nl/2011/01/monads-in-c-5-maybe.html. (4认同)
  • 我发现这有点令人不安,每个答案都呼应了同一个谚语:“如果它很特别,那就扔。” 大多数工程师都会熟悉这个原则。在我看来,这里真正的问题是如何确定它是否*应该*被视为例外。例如,OP 正在寻找与存储库模式相关的*最佳实践*。在给定主键的情况下,对象不存在通常被认为是例外吗?是的,这是他的领域将决定的事情,但大多数拥有多年经验的专家有何建议?这就是我们应该看到的答案类型。 (3认同)
  • @crush我认为指导原则是_将什么参数传递给function_。如果传递_identity_(例如您提到主键),则如果未找到该条目,则应将其视为例外,因为它表示系统中的状态不一致。示例:如果该人已被删除,则将抛出“ GetPersonById(25)”,但“ GetPeopleByHairColor(“ red”)`将返回空结果。因此,我认为参数说明了期望值。 (3认同)
  • 在@ErwinRooijakkers _design选择方式中,从Java 8开始,您还可以返回[Optional <T>](https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html) (2认同)

Car*_*nke 96

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

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

  • 我不同意.您可以将异常作为状态代码抛出:"NotFoundException" (4认同)
  • 我认为处理空情况比“ NotFoundException”要容易得多。考虑一下您必须为抛出“ NotFoundException”的每个单个检索请求编写多少行try-catch。在我看来,准备好所有这些代码很痛苦。 (2认同)
  • 我想引用托尼·霍尔的话:“我称之为我的十亿美元错误”。我不会返回 null,我要么抛出异常并正确处理它,要么返回一个空对象。 (2认同)

Mat*_*ino 66

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

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

  • 我打算投票,因为WTF选项对我来说似乎很棒......但显然我有一颗心 (14认同)
  • 抛出新的WtfExceptin (5认同)

Kev*_*ale 50

如果null从不指示错误,则返回null.

如果null始终是错误,则抛出异常.

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

很难滥用Try例程.忘记检查null是很容易的.

所以当null是你刚才写的错误时

object o = FindObject();
Run Code Online (Sandbox Code Playgroud)

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

if (TryFindObject(out object o)
  // Do something with o
else
  // o was not found
Run Code Online (Sandbox Code Playgroud)

  • 在我看来,尝试方法是最好的方法.您不必查找如果无法返回对象会发生什么.使用Try方法,您可以立即知道该怎么做. (2认同)

Len*_*mel 25

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

  1. 返回null
  2. 抛出异常
  3. 使用null对象模式
  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);
Run Code Online (Sandbox Code Playgroud)

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

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

  • 使用`Expected <T> findObject(String)`可以更清晰地写出大部分内容.其中`Expected <T>`具有函数`orNull()`,`orThrow()`,`orSupplied(Supplier <T>供应商) `,`或默认(T默认)`.这抽象了从错误处理中获取数据 (5认同)

pml*_*que 18

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

  • 如果使用空对象模式,那么如何将键映射到空对象的情况与密钥没有映射的情况区分开来?我认为返回一个没有意义的对象会比返回null更糟糕.将null返回到未准备好处理它的代码通常会导致抛出异常.不是例外的最佳选择,但仍然是例外.返回无意义的对象更可能导致代码错误地将无意义的数据视为正确. (3认同)
  • null对象如何为实体查找行为?例如,`Person somePerson = personRepository.find("does-not-exist");`让我们假设这个方法返回ID为"不存在"的空对象.那么`somePerson.getAge()`的正确行为是什么?现在,我还不确定空对象模式是实体查找的正确解决方案. (3认同)

dmc*_*kee 13

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


Cor*_*erg 11

这取决于您的语言和代码是否促进:LBYL(在您跳跃之前看)或EAFP(比请求更容易请求宽恕)

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

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


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

  • 在某些情况下,EAFP是唯一有意义的方法.例如,在并发映射/字典中,无法询问请求时是否存在映射. (2认同)

Ala*_*anR 11

只要问自己:"这是一个特殊情况,没有找到对象"?如果预计会在程序的正常过程中发生,则可能不应该引发异常(因为它不是异常行为).

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

-Alan.


小智 11

抛出异常的优点:

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

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

  • +1是因为点2非常好-null的含义与未找到的含义相同。当处理动态语言(其中Null实际上可能是该函数存储/检索的对象)时,这变得尤为重要。 (2认同)

aku*_*uhn 5

例外与合同设计有关.

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

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

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

is_present(key)
find(key) throws Exception
Run Code Online (Sandbox Code Playgroud)

IF和ONLY如果你提供第二个合同的两种方法,你可以抛出一个例外,没有找到任何东西!