提问人: 提问时间:10/7/2008 最后编辑:7 revs, 5 users 39%Robert 更新时间:8/6/2022 访问量:194391
检索方法是否应该返回“null”或在无法生成返回值时引发异常?[已结束]
Should a retrieval method return 'null' or throw an exception when it can't produce the return value? [closed]
问:
我正在使用java语言,我有一个方法,如果找到一个对象,它应该返回一个对象。
如果找不到,我应该:
- 返回 null
- 引发异常
- 其他
哪个是最好的做法或成语?
答:
我更喜欢只返回一个 null,并依靠调用方来适当地处理它。(因为缺乏更好的词)例外是,如果我绝对“确定”此方法将返回一个对象。在这种情况下,失败是例外的,应该而且应该抛出。
与您正在使用的 API 保持一致。
使用 null 对象模式或引发异常。
评论
Person somePerson = personRepository.find("does-not-exist");
does-not-exist
somePerson.getAge()
如果客户端代码必须知道 found 和 not found 之间的区别,并且这应该是一种例行行为,那么最好返回 null。然后,客户端代码可以决定要执行的操作。
通常,它应该返回 null。调用该方法的代码应决定是引发异常还是尝试其他操作。
这实际上取决于你是否希望找到这个对象。如果你遵循一个思想流派,即应该使用异常来表示某事,那么,呃,异常已经发生了:
- 找到对象;返回对象
- 未找到对象;抛出异常
否则,返回 null。
仅当确实是错误时才会抛出异常。如果对象不存在是预期行为,则返回 null。
否则,这是一个偏好问题。
评论
如果您总是期望找到一个值,那么如果缺少该值,则引发异常。例外意味着存在问题。
如果该值可能缺失或存在,并且两者都对应用程序逻辑有效,则返回 null。
更重要的是:你在代码的其他地方做了什么?一致性很重要。
评论
GetPersonById(25)
GetPeopleByHairColor("red")
作为一般规则,如果该方法应始终返回一个对象,则使用异常。如果您预计偶尔会出现 null 并希望以某种方式处理它,请使用 null。
无论你做什么,我都强烈建议你不要使用第三种选择:返回一个写着“WTF”的字符串。
评论
取决于找不到对象的含义。
如果这是正常状态,则返回 null。这只是偶尔可能发生的事情,呼叫者应该检查它。
如果是错误,则抛出异常,调用方应决定如何处理丢失对象的错误条件。
最终,这两种方法都会起作用,尽管大多数人通常认为只有在发生异常时才使用异常是一种很好的做法。
评论
只要它应该返回对对象的引用,返回 NULL 就应该很好。
但是,如果它返回整个血腥的东西(就像在C++中一样,如果你这样做:“返回blah;”而不是“return &blah;”(或者“blah”是一个指针),那么你不能返回 NULL,因为它不是“object”类型。在这种情况下,抛出异常或返回未设置成功标志的空白对象是我解决问题的方式。
返回 null 而不是引发异常,并在 API 文档中清楚地记录 null 返回值的可能性。如果调用代码不遵循 API 并检查 null 大小写,则无论如何都可能导致某种“null 指针异常”:)
在 C++ 中,我可以想到 3 种不同的设置方法来查找对象。
选项 A
Object *findObject(Key &key);
找不到对象时返回 null。很好,很简单。我会选择这个。下面的替代方法适用于不讨厌 out-params 的人。
选项 B
void findObject(Key &key, Object &found);
传入对将接收对象的变量的引用。当找不到对象时,该方法将引发异常。如果一个对象不是真的被找到,那么这个约定可能更合适——因此你抛出一个异常来表示这是一个意外的情况。
选项C
bool findObject(Key &key, Object &found);
当找不到对象时,该方法返回 false。与选项 A 相比,这样做的优点是您可以在一个明确的步骤中检查错误情况:
if (!findObject(myKey, myObj)) { ...
不要以为有人提到异常处理中的开销 - 需要额外的资源来加载和处理异常,所以除非它是真正的应用程序终止或进程停止事件(向前发展弊大于利),否则我会选择传回一个值调用环境可以解释它认为合适的。
我同意这里似乎是共识(如果“未找到”是正常的可能结果,则返回 null,或者如果情况的语义要求始终找到对象,则抛出异常)。
但是,根据您的具体情况,还有第三种可能性可能有意义。您的方法可以在“未找到”条件下返回某种默认对象,从而确保调用代码始终接收有效对象,而无需 null 检查或异常捕获。
如果 null 从不表示错误,则返回 null。
如果 null 始终是错误,则引发异常。
如果 null 有时是异常,则编写两个例程。一个例程引发异常,另一个例程是布尔测试例程,它在输出参数中返回对象,如果未找到对象,则例程返回 false。
很难滥用 Try 例程。忘记检查 null 真的很容易。
因此,当 null 是一个错误时,您只需编写
object o = FindObject();
当 null 不是错误时,您可以编写如下代码
if (TryFindObject(out object o)
// Do something with o
else
// o was not found
评论
find
findOrFail
TryFindObject
或返回 Option
选项基本上是一个容器类,它强制客户端处理展位案例。Scala 有这个概念,查一下它的 API。
然后,你在这个对象上有像 T getOrElse(T valueIfNull) 这样的方法,要么返回找到的对象,要么返回客户端指定的 allterion。
返回一个 null,异常就是这样:你的代码做了一些意想不到的事情。
这取决于您的语言和代码是否促进: LBYL(跳跃前先看) 或 EAFP(请求宽恕比请求许可更容易)
LBYL 说你应该检查值(所以返回一个 null)EAFP 说只是尝试该操作,看看它是否失败(抛出异常)
虽然我同意上面的观点。异常应用于异常/错误条件,使用检查时最好返回 null。
Python 中的 EAFP 与 LBYL:
http://mail.python.org/pipermail/python-list/2003-May/205182.html(Web 存档)
评论
在某些函数中,我添加了一个参数:
..., bool verify = true)
True 表示抛出,false 表示返回一些错误返回值。这样,使用此功能的人就有这两种选择。默认值应为 true,以方便那些忘记错误处理的人。
只要问问自己:“找不到对象是例外情况吗”?如果它预计会在程序的正常过程中发生,您可能不应该引发异常(因为它不是异常行为)。
简短版本:使用异常来处理异常行为,而不是处理程序中的正常控制流。
-艾伦。
例外情况应为例外情况。如果返回 null 有效,则返回 null。
评论
例外情况与合同设计有关。
对象的接口实际上是两个对象之间的约定,调用方必须满足约定,否则接收方可能会因异常而失败。有两种可能的合同
1)所有输入方法均有效,在这种情况下,当找不到对象时必须返回null。
2)只有一些输入是有效的,即导致找到对象的输入。在这种情况下,您必须提供第二种方法,允许调用方确定其输入是否正确。例如
is_present(key)
find(key) throws Exception
当且仅当您提供第二个合约的两种方法时,您才被允许抛出异常,因为什么都没有找到!
这里还有一些建议。
如果返回集合,请避免返回 null,返回空集合,这样可以使枚举更易于处理,而无需先进行 null 检查。
一些 .NET API 使用 thrownOnError 参数的模式,该参数为调用方提供了选择,如果找不到对象,则是否真的是异常情况。Type.GetType 就是一个示例。BCL 的另一种常见模式是 TryGet 模式,其中返回布尔值并通过输出参数传递值。
在某些情况下,您还可以考虑 Null 对象模式,该模式可以是默认模式,也可以是没有行为的版本。关键是避免在整个代码库中进行空检查。有关详细信息,请参阅此处 链接
我只是想概括一下前面提到的选项,并加入一些新的选项:
- 返回 null
- 抛出异常
- 使用 null 对象模式
- 提供 Boolean 参数 to you 方法,以便调用方可以选择是否希望你引发异常
- 提供一个额外的参数,以便调用方可以设置一个值,如果找不到值,他将返回该值
或者,您可以组合以下选项:
提供 getter 的多个重载版本,以便调用方可以决定走哪条路。在大多数情况下,只有第一个算法具有搜索算法的实现,而其他算法只是围绕第一个算法:
Object findObjectOrNull(String key);
Object findObjectOrThrow(String key) throws SomeException;
Object findObjectOrCreate(String key, SomeClass dataNeededToCreateNewObject);
Object findObjectOrDefault(String key, Object defaultReturnValue);
即使您选择只提供一种实现,您也可能希望使用这样的命名约定来阐明您的协定,如果您决定添加其他实现,它也会对您有所帮助。
你不应该过度使用它,但它可能会有所帮助,特别是在编写一个帮助程序类时,你将在数百个不同的应用程序中使用它,这些应用程序具有许多不同的错误处理约定。
评论
Expected<T> findObject(String)
Expected<T>
orNull()
orThrow()
orSupplied(Supplier<T> supplier)
orDefault(T default)
仅指 null 不被视为异常行为的情况,我绝对支持 try 方法,很明显,没有必要像这里所说的那样“读书”或“在跳跃之前先看”
所以基本上:
bool TryFindObject(RequestParam request, out ResponseParam response)
这意味着用户的代码也将一目了然
...
if(TryFindObject(request, out response)
{
handleSuccess(response)
}
else
{
handleFailure()
}
...
首选返回 null --
如果调用方在不检查的情况下使用它,则异常无论如何都会发生。
如果来电者没有真正使用它,请不要向他征税 a / blocktry
catch
不幸的是,JDK 不一致,如果您尝试访问资源包中不存在的密钥,则会收到未找到异常,并且当您从 map 请求值时,如果它不存在,则为 null。因此,我会将获胜者答案更改为以下内容,如果找到的值可以为 null,则在未找到时引发异常,否则返回 null。因此,请遵循规则,但有一个例外,如果您需要知道为什么找不到值,请始终引发异常,或者..
抛出异常的优点:
- 调用代码中的控制流更清晰。检查 null 会注入一个条件分支,该分支由 try/catch 本机处理。检查 null 并不表示您正在检查它 - 您检查 null 是因为您正在寻找您期望的错误,还是您检查 null 以便您不会在下链上进一步传递它?
- 消除“null”含义的歧义。null 是否代表错误,还是 null 实际存储在值中?很难说,当你只有一件事可以作为决心的基础时。
- 改进了应用程序中方法行为之间的一致性。异常通常在方法签名中公开,因此您能够更好地了解应用程序中的方法考虑了哪些边缘情况,以及应用程序可以以可预测的方式对哪些信息做出反应。
有关示例的更多说明,请参阅:http://metatations.com/2011/11/17/returning-null-vs-throwing-an-exception/
评论
如果该方法返回一个集合,则返回一个空集合(如上所述)。但请不要Collections.EMPTY_LIST之类的!(如果是 Java)
如果该方法检索单个对象,则您有一些选择。
- 如果该方法应该始终找到结果,并且找不到对象是一个真正的异常情况,那么您应该抛出一个异常(在 Java 中:请一个未选中的异常)
- (仅限 Java)如果可以容忍该方法引发已检查的异常,请引发特定于项目的 ObjectNotFoundException 等。在这种情况下,编译器会提示您是否忘记处理异常。(这是我在 Java 中对未找到的东西的首选处理方式。
- 如果你说它真的没问题,如果找不到对象并且你的方法名称类似于 findBookForAuthorOrReturnNull(..),那么你可以返回 null。在这种情况下,强烈建议使用某种静态检查或编译器检查,以防止在没有 null 检查的情况下取消引用结果。在 Java 的情况下,它可以是例如。FindBugs(请参阅 http://findbugs.sourceforge.net/manual/annotations.html 中的 DefaultAnnotation)或 IntelliJ-Checking。
如果您决定返回 null,请小心。如果你不是项目中唯一的程序员,你将在运行时获得 NullPointerExceptions(Java 或其他语言)!因此,不要返回在编译时未检查的 null。
评论
null
如果您使用的是引发异常的库或其他类,则应重新抛出它。下面是一个示例。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();
}
}
}
例子:.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;
}
}
}
例子2:.java
/**
* Example2.java
* @author Seval
*
*/
public class Example2 {
/**
* Constructor of Example2
* @throws Exception
*/
public Example2() throws Exception{
throw new Exception("Please set the \"obj\"");
}
}
评论