如果null很糟糕,那为什么现代编程语言还要实现它?

网友问题

我很确定像Java 和 C# 的设计者知道 null 引用可能会导致的问题(参见这篇文章: null 引用真的是一个不好的东西吗?) 并且,实现一个可选类型并不比 null 引用要复杂多少。

那他们为什么要设计 null 呢?我确定对语言的创造人员和使用者(尤其是库设计人员)来说,没有 null 引用能推进(或强制性带来)更好的代码质量。

难道仅仅是因为保守主义:其他语言是这样的,所以我们也应该有?

Eric Lippert 的回复

“我很确定 Java 和 C# 语言的设计者是知道 null 引用存在的一些问题。”

当然。

“实现一个可选类型并不比 null 引用复杂多少。”

恕我不能苟同! C# 2 中引入空值类的设计考虑是很复杂、有争议并且困难的。他们让语言和运行时的设计组进行了数月的争论、原型实现以及其它一些工作,而事实上,空值包装类型的语义被改变的几乎要颠覆 C# 2.0 ,因此引起了很大争议。

那他们为什么还决定引入它呢?

语言的设计也就是一个在或多或少不相容的众多目标中做选择。我这里简单列举一些可能被考虑的因素:

  • 语言特征具有正交性普遍认为是好的。C# 有空值类型、非空值类型和空引用类型,而不存在非空引用类型,这就让类型系统成为非正交的。
  • 要让 C、C++和 Java 的现有用户的容易熟悉很重要。
  • 易与 COM组件互操作很重要 。
  • 与其他 .NET 语言易交互很重要。
  • 与数据库的能够相互协同工作很重要。
  • 语义一致性很重要;如果 TheKingOfFrance 引用的值为空,并不意味着现在法国一定没有国王;我只是暂且不知道是谁而已。或者也能表达“法国有国王的这个想法很荒谬”的意思,所以别问这个问题!在 C# 中,null 能够表达以上所有的意思甚至更多,而这些意思都是有用的。
  • 性能开销很重要。
  • 能否经过静态分析的检验很重要。
  • 类型系统的一致性非常重要;我们能确定非空引用在任何情况下都不是无效的吗?那怎么解释对象构造函数中引用类型的非空字段?还有,怎么解释这样一个对象在它的finalizer(对象被终结的地方),由于代码本应实现一个引用却没有导致抛出了异常?一个取决于编程人员来保证安全的类型系统是很危险的。
  • 语义一致性呢?空值在被使用时能够被传递,而空引用在使用时会抛出异常。这个不一致要用哪些它带来的益处来正名呢?
  • 我们能够能够实现一个不会破坏其他任何特性的特性吗?即使可以,那又怎么能预防以后特性的实现不会呢?
  • 你去打仗的时候是带着你有的军队而不是你喜欢的。记住, C#1.0 并没有泛型,所以讨论 Maybe<T> 作为候选方案是完全不值得考虑的。如果运行团队添加泛型,.NET 可能要走两年下坡路,仅仅为了消除 null 引用,这值吗?
  • 类型系统一致性呢?你也许会说 Nullable<T> 可以用于任何值类型 — 不,等等,你错了。你总不能用 Nullable<Nullable<T>>,是吧? 如果要用这个,表示的应该是什么意思?仅仅为了这个特征而让整个类型系统拥有这样一个特例值吗?

还有很多原因。这些决定是很麻烦的。

Doval 的最佳回复

免责声明: 由于我并不认识语言的设计人员,所以我的回答仅仅是我的推测。

Tony Hoare 自己说过:

我将1965年发明的 null 称作我百万美元的错误。那个时候,我正在设计第一个面向对象语言(ALGOLW)中的引用的综合类型系统。我的目标是确保所有引用的使用都绝对安全,由编译器自动执行检查确保安全。但我无法抵挡放入空引用的诱惑,仅仅因为这个很容易实现。这个决定导致了不计其数的错误、漏洞和系统崩溃,在其后的四十年可能已经造成了百万美元的损失。

说一下我的想法:

当然在那个时候设计空引用并不是一个坏主意。这可能也是它一直被延续的原因—如果快速排序的发明者得到图灵奖是非常合适的,那么很多人仍然不理解 null 的危害并不奇怪。也有部分是因为,不管从市场还是学习曲线上来讲设计新的语言和旧的相似非常便捷。针对这点的一个案例:

“我们追随着 C++ 的程序员。设法将他们中的大部分人拖向 Lisp 语言。”- Guy Steele ,Java 规范的联合作者。(Source: http://www.paulgraham.com/icad.html

而且,当然,C++ 中有 null 是因为 C 中有, 也没有必要去追溯 C 的历史为什么引入 null 。 C# 是 J++ 的替代, 微软对 Java 的实现, 也是为了让它取代 C++ 作为微软开发语言的首选,所以,它可能是从任何语言的设计中引入了 null 。

另外 Hoare 的话也值得考虑一下:

编程语言整体上比曾经复杂得多:面向对象、集成,还有其他一些特性都还没有从一个连贯、基于科学原理或正确性理论的角度认真考虑过。我最初的要求,也是我作为科学家毕生所追求的,就是使用正确性理论来收敛一个好的编程语言的设计,这样才不会为它的使用者留下坑,程序的不同组件才能清晰正确的对应到它规格说明中的部分,这样才能进行合理正确的思考。包括编译器等一些工具,也必须要基于写出正确代码的理论来设计。

另外,再强调一下我的看法。 Sun/Oracle 和微软都是公司,公司的底线就是钱。包含 null 可能是利大于弊的,不然就是他们的时间太紧而不能认真推敲这个问题。这里介绍一个由于时间紧迫而造成语言出错的例子:

很可惜 Cloneable 失效了,但确实就这样发生了。由于市场窗口即将关闭,原始 Java API 的开发时间非常短。最开始的 Java 团队做得非常出色,但是并不是所有的 API 都是完美的。 Cloneable 是一个弱点,我觉得大家必须意识到它的局限性。 – Josh Bloch (Java 大牛)

本文文字及图片出自 伯乐在线

余下全文(1/3)
分享这篇文章:

请关注我们:

共有 1 条讨论

  1. KONGbo  这篇文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注