我知道这个问题已经有了一个被接受的答案。但是由于我进行的讨论,我决定写下自己的答案来解决这个问题。
NULL的发明者Tony Hoare先生说过:
“我称它为我的十亿美元错误。这是1965年引入null引用的发明。那时,我正在设计面向对象语言(ALGOL W)中引用的第一个全面类型系统。我的目标是确保所有引用的使用都是绝对安全的,并由编译器自动执行检查。但我无法抵制放置空引用的诱惑,仅仅因为它很容易实现。这导致了无数的错误、漏洞和系统崩溃,可能在过去四十年里造成了十亿美元的痛苦和损失。”
首先,在更新已发布和测试过的旧代码时引入破坏性更改是非常不好的做法。在这种情况下进行重构总是很危险的,应该避免。我知道发布丑陋或愚蠢的代码可能会伤害你的感情,但如果出现这种情况,那么这清楚地表明缺乏质量管理工具(例如代码审查或规范)。但显然没有单元测试,否则作者会由于测试失败而立即撤销更改。主要问题在于你的团队没有约定如何处理NULL作为参数或结果。更新者将从NULL参数切换到空集合的意图是绝对正确的,应该得到支持,但他不应该在工作代码中这样做,并且他应该与团队讨论,以便团队的其他成员可以跟进,以使其更有效。你的团队必须一致同意放弃将NULL作为参数或结果值,或至少找到一个标准。最好把NULL送到地狱去。你唯一的解决方案是撤销更新作者所做的更改(我假设你正在使用版本控制)。然后,用旧式且肮脏的方式使用NULL进行更新,就像以前一样。不要在新代码中使用NULL-为了更美好的未来。试图修复已更新的版本肯定会加剧情况,因此会浪费时间。(我假设我们正在谈论一个更大的项目)。如果可能的话,请回滚到上一个版本。
简言之,如果您不喜欢继续阅读:是的,这是非常糟糕的做法。您自己可以从目前的情况中得出这个结论。现在,您目睹了非常不稳定和不可预测的代码。无理性的错误是证明您的代码变得不可预测的证据。如果您至少没有业务逻辑的单元测试,那么代码就像热铁一样。导致这种代码的做法永远不可能是好的。NULL对API的用户没有直观意义,当有可能得到中性结果或参数时(例如集合的情况),则应该优先考虑使用中性结果或参数。空集合是中性的。您可以期望一个集合是空的或至少包含一个元素。从集合中您不能期望其他任何东西。你想表明一个错误和一个操作无法终止?那么最好抛出一个带有清晰根源的良好名称的异常,以向调用者清楚地传达错误的根本原因。
NULL是一个历史遗留物。在程序员跌倒时,NULL值意味着程序员编写了松散的代码。他忘记通过将内存地址赋值给指针来初始化指针。编译器必须知道内存地址作为参考,以创建有效的指针。如果你想要一个指针但不想浪费内存仅用于声明指针或者此时还不知道地址,则NULL是一种约定使得指针指向不存在的地方,这意味着不必为指针的引用分配内存(除了指针本身)。如今,在具有垃圾回收和大量可用内存的现代面向对象语言中,NULL已经变得不相关了。存在某些情况(如在SQL中)需要使用它以表示缺少数据。但在面向对象编程中,您完全可以避免使用它,从而使您的应用程序更加健壮。
您可以选择应用Null-Object模式。此外,最佳实践是使用默认参数(如果您的语言支持),或使用重载。例如,如果方法参数是可选的,则使用重载(或默认参数)。如果该参数是强制性的,但您无法提供值,则只需不调用该方法。如果该参数是集合,则在没有值的情况下始终使用空集合。方法的作者必须处理此情况,因为他必须处理所有可能的情况或参数状态,包括NULL。因此,方法的作者有责任检查NULL并决定如何处理它。这就是惯例发挥作用的地方。如果您的团队同意永远不使用NULL,则不再需要进行繁琐且丑陋的空值检查。一些框架提供
@NotNull
属性。作者可以使用它来装饰方法参数以指示NULL不是有效值。编译器现在将执行NULL检查并向(误用)该方法的程序员显示错误消息,以防止或识别违规操作,并导致更加健壮的代码。
大多数库都会提供帮助类,例如
Array.Empty()
或
Enumerable.Empty()
(C#),用于创建提供
IsEmpty()
等方法的空集合。这使意图在语义上变得清晰,从而使代码易于阅读。如果您的标准库中没有这样的帮助程序,则编写自己的帮助程序是值得的。
尝试将质量管理纳入团队的常规工作流程中。进行代码审查,以确保要发布的代码符合您的质量标准(单元测试,无NULL值,始终对语句体使用大括号,命名等)。
我希望你能解决问题。我知道清理他人的混乱是一种有压力的情况。这就是为什么团队间的沟通如此重要的原因。
getPersons()
返回一个person
集合时,在什么情况下您会期望该集合为NULL?如果没有人,则集合为空。调用者则知道“啊哈,没有找到人”。对于调用者来说,NULL意味着什么?什么也不意味着。在出现错误的情况下,最好抛出异常。这更有意义。 - BionicCode