返回null坏devise?

我听到一些声音说检查从方法返回的空值是不好的devise。 我想听听这个原因。

伪代码:

variable x = object.method() if (x is null) do something 

不返回null的基本原理是你不必检查它,因此你的代码不需要遵循基于返回值的不同path 。 您可能想要查看提供更多信息的空对象模式 。

例如,如果我要在Java中定义一个返回集合的方法,我通常更喜欢返回一个空的集合(即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. } } 

这是原因。

在罗伯特·马丁(Robert Martin)的Clean Code中,他写道,当你可以返回空数组时,返回null是不好的devise。 由于预期的结果是一个数组,为什么不呢? 它将使您能够在没有任何额外条件的情况下迭代结果。 如果它是一个整数,也许0就足够了,如果它是一个散列,空散列。 等等

前提是不要强制调用代码来立即处理问题。 调用代码可能不想关心他们自己。 这也是为什么在许多情况下,例外情况比零好。

返回null的好用处:

  • 如果null是有效的function结果,例如:FindFirstObjectThatNeedsProcessing()如果未find,则返回null,调用者应该相应地进行检查。

不好的用途:试图replace或隐藏特殊情况,例如:

  • catch(…)并返回null
  • API依赖项初始化失败
  • 磁盘空间不足
  • input参数无效(编程错误,input必须由调用者清理)
  • 等等

在这些情况下抛出一个exception是更充分的,因为:

  • 空返回值不提供有意义的错误信息
  • 直接调用者很可能无法处理错误情况
  • 不能保证调用者正在检查空结果

但是,不应该使用exception来处理正常的程序操作条件,例如:

  • 无效的用户名/密码(或任何用户提供的input)
  • 打破循环或作为非本地gotos

谁说这是不好的devise?

检查空值是一种常见的做法,甚至是被鼓励的,否则在任何地方都会冒NullReferenceExceptions的风险。 它更好地处理错误,而不是在不需要的时候抛出exception。

是的,在面向对象的世界里,返回NULL是一个糟糕的devise 。 简而言之,NULL用法导致:

  • 临时error handling(而不是例外)
  • 模棱两可的语义
  • 慢而不是快速失败
  • 电脑思维而不是对象思维
  • 可变和不完整的对象

检查这个博客文章的详细解释: http : //www.yegor256.com/2014/05/13/why-null-is-bad.html 。 更多在我的书优雅的对象 ,4.1节。

根据你到目前为止所说的话,我认为没有足够的信息。

从CreateWidget()方法返回null看起来很糟糕。

从FindFooInBar()方法返回null似乎很好。

它的发明者说这是十亿美元的错误!

这取决于你使用的语言。 如果你使用的是像C#这样的语言,那么表示缺less值的惯用方法是返回null,那么如果没有值,则返回null是一个好的devise。 或者,在Haskell这种惯用的Maybe monad语言的语言中,返回null将是一个糟糕的devise(如果甚至可能的话)。

如果这样做有意义,可以返回null:

 public String getEmployeeName(int id){ ..} 

在这种情况下,如果id与现有实体不一致,则返回空值是有意义的,因为它允许您区分未发现匹配与合法错误的情况。

人们可能会认为这是不好的,因为它可能被滥用作为一个“特殊的”返回值,表示错误条件,这是不是很好,有点像从函数返回错误代码,但令人困惑,因为用户必须检查返回null,而不是捕捉适当的例外,例如

 public Integer getId(...){ try{ ... ; return id; } catch(Exception e){ return null;} } 

这不一定是一个糟糕的devise – 就像这么多的devise决定一样,这取决于。

如果方法的结果是在正常使用中没有好的结果,则返回null是很好的:

 object x = GetObjectFromCache(); // return null if it's not in the cache 

如果真的应该总是有一个非空的结果,那么抛出exception可能会更好:

 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 /* ... */ { } 

如果你阅读所有的答案,就很清楚,这个问题的答案取决于方法的types。

首先,当exception情况发生时(IO问题等),会引发逻辑exception。 什么东西是特殊的时候可能是一个不同的话题..

每当一个方法预计可能没有结果时,有两个类别:

  • 如果有可能返回一个中性值,那就这样做
    空的enumrables,string等是很好的例子
  • 如果这个中性值不存在,则返回null
    如前所述,该方法被认为可能没有结果,所以它不是例外,因此不应该抛出exception。 一个中性值是不可能的(例如:0不是一个中立的结果,取决于程序)

直到我们有一个官方的方式来表示一个函数可以或不可以返回null,我尝试有一个命名约定来表示。
就像你对那些预期会失败的方法使用Try Something()约定一样,当方法返回一个中立的结果而不是null时,我经常将我的方法命名为Safe Something()

我还没有完全确定这个名字,但不能拿出更好的东西。 所以我现在正在运行。

你看到什么替代方法返回null?

我看到两种情况:

  • findAnItem(id)。 如果找不到该项目,该怎么办?

在这种情况下,我们可以:返回Null或抛出(检查)exception(或者创build一个项目并返回)

  • listItemsMatching(条件)如果没有发现什么,这应该返回什么?

在这种情况下,我们可以返回Null,返回一个空列表或抛出一个Exception。

我相信返回null可能不如替代品好,因为它需要客户端记住检查null,程序员忘记和代码

 x = find(); x.getField(); // bang null pointer exception 

在Java中,抛出一个检查exceptionRecordNotFoundException,允许编译器提醒客户端处理大小写。

我发现,返回空列表的search可以非常方便 – 只需填充列表中的所有内容的显示,哦,它是空的,代码“正常工作”。

例外情况是例外情况。

如果函数的目的是查找与给定对象相关联的属性,并且该对象没有这样的属性,则返回null可能是合适的。 如果对象不存在,抛出exception可能更合适。 如果函数的目的是返回一个属性列表,而且没有返回值,那么返回一个空的列表是有意义的 – 你将返回所有的零属性。

我在这方面有一个公约,这对我很好

对于单项查询:

  • Create...返回一个新的实例, 或抛出
  • Get...返回一个预期的现有实例, 或抛出
  • GetOrCreate...返回一个现有的实例,或者新的实例,如果不存在或抛出
  • Find...返回一个存在的实例, 或者返回null

对于收集查询:

  • Get...总是返回一个集合,如果没有find匹配的[1]项目,这个集合是空的

[1]给出了一些标准,显式或隐式,在函数名中给出或作为参数。

对于某些情况,只要发生故障就要注意失败。

在失败情况下检查NULL和不断言(对于程序员错误)或抛出(对于用户或调用者错误)可能意味着后来的崩溃更难追查,因为没有find原始的奇怪情况。

而且,忽略错误会导致安全漏洞。 也许是因为缓冲区被覆盖等原因而导致无效。 现在,你不会崩溃,这意味着利用者有机会在你的代码中执行。

有时,返回NULL是正确的做法,但是特别是在处理不同types的序列时(数组,列表,string,还有什么),最好返回一个零长度的序列,因为它导致更短,希望更易于理解的代码,而不会在API实现者方面做更多的工作。

让他们调用另一个方法之后,找出以前的调用是否为空。 ;-)嘿,这对于JDBC来说已经足够了

这个线程背后的基本思想是防守编程。 那就是代码反对意外。 有一系列不同的答复:

Adamskibuild议查看空对象模式,并且对这个build议投了赞成票。

迈克尔·瓦伦蒂(Michael Valenty)也提出了一个命名约定来告诉开发者可能会有什么样 如果这是NULL的原因, ZeroConceptbuild议正确使用Exception。 和别的。

如果我们制定我们总是想做防御性编程的“规则”,那么我们可以看到这些build议是有效的。

但是我们有两个开发场景。

开发人员撰写的类:作者

另一个(也许)开发者“消费”的类:开发者

无论类是否返回具有返回值的方法的NULL,开发人员都需要testing该对象是否有效。

如果开发人员不能这样做,那么这个类/方法是不确定的。 也就是说,如果获得对象的“方法调用”没有做“广告”(比如getEmployee),那就违反了合同。

作为一个class级的作者,我总是希望在创build一个方法的时候能够performance出善意和防御(和确定性)。

因此,如果需要检查NULL或NULL OBJECT(例如,如果(雇员为NullEmployee.ISVALID)),并且可能需要与Employees集合一起发生,则空对象方法是更好的方法。

但是我也喜欢Michael Valenty提出的命名方法,比如必须返回null,例如getEmployeeOrNull。

引发exception的作者将删除开发人员的select,以testing对象集合的有效性,这对对象集合是非常不利的,并且在分支消费代码时强制开发人员进行exception处理。

作为一名开发人员,我希望作者能让我避免或计划他们的类/方法可能面临的空情况。

所以作为一名开发人员,我会防御性地从一个方法中对NULL进行编程。 如果作者给了我一个总是返回一个对象的合约(NULL OBJECT总是这样做),并且该对象有一个方法/属性来testing对象的有效性,那么我将使用该方法/属性继续使用该对象,否则该对象是无效的,我不能使用它。

底线是,类/方法的作者必须提供开发人员可以在其防御性编程中使用的机制。 也就是说,这个方法的意图更清楚了。

开发人员应该始终使用防御性编程来testing从另一个类/方法返回的对象的有效性。

问候

GregJF

其他的选项是:返回一些表示成功与否的值(或者错误的types),但是如果你只是需要布尔值来表示成功/失败,返回null表示失败,成功的对象不会不太正确,然后返回true / false并通过参数获取对象。
其他的方法是使用exception来表示失败,但在这里 – 实际上有更多的声音,说这是一个不好的做法(因为使用例外可能是方便的,但有许多缺点)。
所以我个人没有看到返回null的任何错误,表明出了什么问题,并在稍后检查(实际上是否知道你是否成功了)。 另外,一味地认为你的方法不会返回NULL,然后把你的代码放在它上面,可能会导致其他的,有时很难find的错误(虽然在大多数情况下,它只会使你的系统崩溃:),你会参考0x00000000迟早)。

在复杂的程序开发过程中,可能会出现意想不到的空函数,像死代码一样,这种情况表明程序结构存在严重的缺陷。

在对象框架中,经常使用空函数或方法作为可触及函数或可覆盖方法的默认行为。

Null_function @维基百科

那么,这肯定取决于方法的目的…有时候,更好的select是抛出exception。 这一切都依情况而定。

如果代码是这样的:

 command = get_something_to_do() if command: # if not Null command.execute() 

如果你有一个其execute()方法什么都不做的虚拟对象,并且你在适当的情况下返回它而不是Null,你不必检查Null情况,而只需要做:

 get_something_to_do().execute() 

所以,这里的问题不是在检查NULL和exception之间,而是在调用者必须以不同的方式(以任何方式)处理特殊的非情况之间进行。

天儿真好,

当您无法创build新对象时返回NULL是许多API的标准做法。

为什么这是糟糕的devise我不知道。

编辑:这是没有例外的语言,如C多年来的惯例。

HTH

“Avahappy,