我听到一些声音说,从方法中检查返回的空值是错误的设计。我想听到一些原因。
伪代码:
variable x = object.method()
if (x is null) do something
我听到一些声音说,从方法中检查返回的空值是错误的设计。我想听到一些原因。
伪代码:
variable x = object.method()
if (x is null) do something
Answers:
不返回null的基本原理是您不必检查它,因此您的代码无需根据返回值遵循其他路径。您可能想查看Null对象模式,它提供了有关此信息的更多信息。
例如,如果我要用Java定义一个返回Collection的方法,那么我通常希望返回一个空collection(即Collections.emptyList()
)而不是null,因为这意味着我的客户端代码更干净;例如
Collection<? extends Item> c = getItems(); // Will never return null.
for (Item item : c) { // Will not enter the loop if c is empty.
// Process item.
}
...比以下更清洁:
Collection<? extends Item> c = getItems(); // Could potentially return null.
// Two possible code paths now so harder to test.
if (c != null) {
for (Item item : c) {
// Process item.
}
}
null
一个“容器”,其中包含指向对象的零个或一个指针。(这肯定是Haskell Maybe
在这些情况下明确建模的方式,这样做更好。)
这就是原因。
他在Robert Martin的Clean Code中写道,如果可以改为返回空数组,则返回null是错误的设计。由于预期结果是一个数组,为什么不呢?它使您无需任何额外条件即可遍历结果。如果是整数,则可能为0,如果是哈希,则为空哈希。等等
前提是不要强迫调用代码立即处理问题。调用代码可能不想与它们有关。这就是为什么在许多情况下,例外比零更好。
返回null的好用法:
不良用法:尝试替换或隐藏特殊情况,例如:
在这些情况下,抛出异常就足够了,因为:
但是,不应将异常用于处理正常的程序操作条件,例如:
boolean login(String,String)
看起来不错,也是这样AuthenticationContext createAuthContext(String,String) throws AuthenticationException
是的,在面向对象的世界中,返回NULL是一个糟糕的设计。简而言之,使用NULL会导致:
查看此博客文章以获取详细说明:http : //www.yegor256.com/2014/05/13/why-null-is-bad.html。在我的《优雅的物体》第4.1节中有更多内容。
bool TryGetBlah(out blah)
或FirstOrNull()
或MatchOrFallback(T fallbackValue)
。
isEmpty()
),并且只有在返回true时才调用该方法。人们反对第二个说法,那就是它的性能较差-但正如Unix Philosophy所说,“将人的时间与机器时间相提并论”(即,性能的降低比开发人员调试产生虚假错误的代码浪费的时间更少)。
谁说这是不好的设计?
检查空值是一种惯例,甚至鼓励这样做,否则,到处都有NullReferenceExceptions的风险。与不需要时抛出异常相比,优雅地处理错误要好于抛出异常。
根据您到目前为止的发言,我认为没有足够的信息。
从CreateWidget()方法返回null似乎很糟糕。
从FindFooInBar()方法返回null似乎很好。
Create...
返回一个新实例,或throws;Get...
返回一个预期的现有实例,或者抛出;GetOrCreate...
返回一个现有实例,如果不存在则返回一个新实例,否则抛出;Find...
返回一个现有实例(如果存在),或null
。对于集合查询- Get...
始终返回一个集合,如果找不到匹配项,则为空。
它的发明者说这是十亿美元的错误!
这取决于您使用的语言。如果您使用的是C#之类的语言,表示缺少值的惯用方式是返回null,那么如果没有值,则返回null是一个不错的设计。另外,在像Haskell这样的语言中,在这种情况下习惯使用Maybe monad的语言,那么返回null将是一个糟糕的设计(即使可能的话)。
null
C#和Java 之类的语言中的定义经常被重载,并在域中赋予了一定的意义。如果您null
在语言规范中查询关键字,则仅表示“无效指针”。在任何问题领域,这可能毫无意义。
null
还是“未初始化”null
如果您阅读了所有答案,则可以很清楚地看到此问题的答案取决于方法的类型。
首先,当发生异常情况(IO问题等)时,逻辑上会引发异常。当确实有什么例外的时候,可能是针对其他主题的事情。
只要期望一种方法可能没有结果,就会有两类:
直到我们有一种正式的方法来表示一个函数可以或不能返回null为止,我尝试使用一个命名约定来表示它。
就像您对预期会失败的方法具有Try Something()约定一样,当方法返回中性结果而不是null时,我经常将我的方法命名为Safe Something()。
我还不太满意这个名字,但是无法提出更好的建议。所以我现在就开始讨论。
我在这个领域有个惯例对我很有帮助
对于单项查询:
Create...
返回一个新实例,或者抛出Get...
返回预期的现有实例,或抛出GetOrCreate...
返回一个现有实例,如果不存在则返回一个新实例,否则抛出Find...
返回现有实例(如果存在),或者null
对于集合查询:
Get...
总是返回一个集合,如果没有找到匹配的[1]项,则为空[1]在函数名称或参数中给出了一些显式或隐式条件。
Get
我希望它在那里的时间,所以如果它不在那里,那就是一个错误,然后我抛出-我永远不需要检查返回值。Find
如果我真的不知道它是否存在,我会使用-然后我需要检查返回值。
例外是针对例外情况。
如果您的函数旨在查找与给定对象关联的属性,而该对象没有此类属性,则返回null可能是合适的。如果对象不存在,则抛出异常可能更合适。如果该函数旨在返回属性列表,并且没有要返回的属性,则返回一个空列表很有意义-您将返回所有零个属性。
如果这样做在某种意义上是有意义的,则可以返回null:
public String getEmployeeName(int id){ ..}
在这种情况下,如果id与现有实体不对应,则返回null很有用,因为它可以让您区分出从合法错误中找不到匹配项的情况。
人们可能会认为这很糟糕,因为它可能会被滥用为表示错误状态的“特殊”返回值,该返回值并不是很好,有点像从函数返回错误代码,但由于用户必须检查返回值而感到困惑null,而不是捕获适当的异常,例如
public Integer getId(...){
try{ ... ; return id; }
catch(Exception e){ return null;}
}
它不一定是不良的设计-取决于许多设计决策,这取决于设计。
如果该方法的结果不能正常使用,则返回null是可以的:
object x = GetObjectFromCache(); // return null if it's not in the cache
如果确实总是会有一个非空的结果,那么最好抛出一个异常:
try {
Controller c = GetController(); // the controller object is central to
// the application. If we don't get one,
// we're fubar
// it's likely that it's OK to not have the try/catch since you won't
// be able to really handle the problem here
}
catch /* ... */ {
}
Optional<>
对于某些情况,您希望在发生故障时立即注意到它。
检查NULL并在失败情况下不声明(对于程序员错误)或抛出(对于用户或调用者错误)可能意味着以后的崩溃更难追踪,因为未找到原始的奇数情况。
此外,忽略错误可能导致安全漏洞。无效性可能来自缓冲区被覆盖之类的事实。现在,您不会崩溃,这意味着开发者有机会在您的代码中执行。
您看到返回null的其他选择吗?
我看到两种情况:
在这种情况下,我们可以:返回Null或引发一个(已检查的)异常(或者可以创建一个项目并将其返回)
在这种情况下,我们可以返回Null,返回空列表或引发Exception。
我认为,返回null可能不如其他方法好,因为它要求客户端记住检查null,程序员忘记并编写代码
x = find();
x.getField(); // bang null pointer exception
在Java中,抛出已检查的异常RecordNotFoundException可使编译器提醒客户端处理大小写。
我发现返回空列表的搜索非常方便-只需在列表中填充列表的所有内容,哦,它是空的,代码“可以正常工作”。
该线程背后的基本思想是防御性编程。也就是说,针对意外情况进行编码。有一系列不同的答复:
Adamski建议查看“空对象模式”,并对该建议投赞成票。
迈克尔·瓦伦蒂(Michael Valenty)还建议使用命名约定来告诉开发人员可能会发生什么。 如果这是NULL的原因,那么ZeroConcept建议正确使用Exception。和别的。
如果我们做出我们一直想进行防御性编程的“规则”,那么我们可以看到这些建议是有效的。
但是我们有2个开发方案。
由开发人员“创作”的类:作者
由另一个(也许)开发人员“消耗”的类:开发人员
无论类是否为具有返回值的方法返回NULL,开发人员都将需要测试对象是否有效。
如果开发人员无法执行此操作,则该类/方法不确定。也就是说,如果获取对象的“方法调用”没有执行其“通告”的操作(例如getEmployee),则它违反了合同。
作为课程的作者,我总是希望在创建方法时保持友善和防御(确定性)。
因此,鉴于需要检查NULL或NULL OBJECT(例如if(employee as NullEmployee.ISVALID)),并且可能需要对一组Employees进行检查,因此空对象方法是更好的方法。
但是我也喜欢Michael Valenty的建议,即命名必须返回null的方法,例如getEmployeeOrNull。
引发异常的作者正在删除开发人员测试对象有效性的选择,这对一组对象非常不利,并在分支其使用的代码时强制开发人员进行异常处理。
作为开发人员,我希望作者能够给我提供避免或编程他们的类/方法可能遇到的无效情况的能力。
因此,作为开发人员,我将通过一种方法针对NULL进行防御性编程。如果作者给我提供了一个合同,该合同始终返回一个对象(始终为NULL OBJECT)并且该对象具有用于测试对象有效性的方法/属性,那么我将使用该方法/属性来继续使用该对象,否则该对象无效,我将无法使用它。
最重要的是,类/方法的作者必须提供开发人员可以在防御性编程中使用的机制。即,该方法的意图更明确。
开发人员应始终使用防御性编程来测试从另一个类/方法返回的对象的有效性。
问候
格雷格·JF
其他选择包括:返回一些指示成功与否(或错误类型)的值,但是如果您只需要布尔值指示成功/失败,则返回null表示失败,而对象则不表示成功不太正确,然后返回true / false并通过参数获取对象。
另一种方法是使用异常来指示失败,但是在这里-实际上还有很多声音,说这是一种BAD做法(因为使用异常可能很方便,但也有很多缺点)。
因此,我个人认为返回null表示出现问题并随后进行检查(以实际上知道您是否成功),这没有什么不好。同样,一味地认为您的方法将不会返回NULL,然后将其基于代码,可能会导致其他(有时很难找到)错误(尽管在大多数情况下,它将使您的系统崩溃:),因为您将引用早晚为0x00000000)。
在复杂程序的开发过程中可能会出现意外的空函数,并且像死代码一样,这种现象表明程序结构中存在严重缺陷。
空函数或方法通常用作对象框架中可重新引导函数或可重写方法的默认行为。
好吧,它肯定取决于方法的目的。有时,更好的选择是引发异常。这全视情况而定。
对于我的用例,我需要从方法返回一个Map,然后寻找一个特定的键。但是,如果我返回一个空的Map,它将导致NullPointerException,然后返回null而不是一个空的Map不会有太大的不同。但是从Java8开始,我们可以使用Optional。以上正是引入可选概念的原因。
G'day,
当您无法创建新对象时,返回NULL是许多API的标准做法。
为什么不知道这是一个糟糕的设计,我不知道。
编辑:对于没有例外的语言,例如C,多年来的惯例,这都是正确的。
高温超导
'Avahappy,