Null is your friend, not a mistake

原文做者: Roman Elizarovhtml

原文地址: Null is your friend, not a mistakejava

译者:秉心说编程

Kotlin Island from Wikimedia by Pavlikhin, CC BY-SA 4.0api

我使用 Java 语言编程已经好久好久了,掌握了经过 Java 编写和维护大型软件(百万行代码)应该注意些什么,并亲眼目击了全行业都在竭力避免空指针异常 NullPointerException(NPE),它困扰着大大小小的 Java 类库。在 2009 年其发明者 Tony Hoare 认可空引用是他形成的一个 “十亿美圆的错误”以前,人人已经意识到它的危险性。安全

在 1996 年 Java 1.0 发布时,这个问题还不是如此明显。让咱们看一个典型的 Java API 的例子:File.list() 方法。它被用来列举文件夹中的内容,以下所示:性能优化

for(String name : new File("directory").list()) {
    System.out.println(name);
}
复制代码

仅当文件夹存在时上面的代码才会正常运行,不然将抛出 NPE,由于 list() 返回了 null。可是谁会写这样的代码呢?不只仅 list() 方法的文档中清楚的说明了文件夹不存在时将返回 null,并且现代的 IDE 也会对特定的代码给你提出警告。微信

可是,当使用 Java 编程时,开发者常常犯这类错误。到目前为止,有大量研究代表它们是如何发生的。结果代表,大多数状况下,咱们的 API 函数不该该返回 null,其余开发者也并不但愿返回 null。在一些特殊状况下,好比缺省值,Java 中的惯例是返回一些 “空对象”(空集合,未填充的对象等等),或者抛出异常,比返回 null 更糟糕。这就是为何要设计 Files.newDirectoryStream, 高级版本的 File.list,任何状况都不会出现 null。oracle

因此当 null 在一些特殊状况下做为函数返回值时,如性能优化,未初始化的引用字段等等,它经常会让你措手不及,没有作好准备去处理它。不只仅是必须处理空值的状况不多,并且在 Java 中用来处理空值的代码是很啰嗦的:框架

String[] list = new File("directory").list();
if (list != null) {
    for (String name : list) {
        System.out.println(name);
    }
}
复制代码

毫无疑问,除非真的须要(你的客户在生产环境发现了 NPE),否则你真的不想写这样的代码。jvm

对 null 的恐惧致使了一些极端状况。有一些 Java 编码风格彻底禁止 null,将可恶的工做交给开发者。不知道你有没有见过这样的 Java 类库,全部的域对象都要实现一个特殊的 Null 接口,而且提供手动编码生成的 “空对象” 实例。若是没有见过说明你仍是幸运的。可是我敢打赌你已经看到了只为了不空值而污染 Java 代码的 Optional <T> 包装器(译者注:Java 8 新特性)。

有些集合框架的 API 出于谨慎禁止 null 元素,而且一些 Java 核心团队成员认为 Java 集合框架对 null 的支持是一个错误。这让人很是难过。

事实上,null 这个概念不是一个错误,可是 Java 的类型系统认为 null 是任何类型的成员。 让咱们看看,在 Java 中 “abc” 是一个合法的 Stringnull 也是一个合法的 String。你能够在前者上使用 string 的全部方法,例如 substring。可是在后者上使用则会发生运行时错误。它是类型安全的吗?并不彻底是。一个类型的特定值在进行某些操做时发生运行时异常(例如除 0)是正常的,可是当对一个值进行该类型的全部操做都发生了异常,这首先代表的是这个值并不属于这个类型。全部的那些 NPE 都代表了 Java 的类型系统存在明显的缺陷。

更多的类型安全的编程语言,例如 Kotlin,经过合理的将 null 的概念合并到类型系统中来修复这个缺陷。添加检查和警告也有必定做用,但这并不够。显然,一个健全的类型系统必须容许 String 类型的全部变量都支持它的操做。因此在 Kotlin 中,将 null 赋给 String 类型的变量就不只仅只是一个警告了,而是类型错误,就像把数值 42 赋给 String 类型变量同样。

类型系统中合理的 null 支持是 API 设计的一个转折点,没有任何理由再去惧怕 null 了。一些函数返回可空类型 String?,另外一些函数返回不可空类型 String,就和一些函数返回 String,另外一些返回 Int 同样。它们都是不一样的类型,有着不一样可是安全的操做集。

用类型安全的 null 来表示 “缺失的值” 是更好,更高效,更简洁的。看一下 Kotlin 标准库中的 String.toIntOrNull() 函数,用于将 string 转为数字,不能转换的话返回 null。使用起来很愉快,编写一个命令行程序,接受 integer 参数并处理参数的缺失就很简单:

fun main(args: Array<String>) {
    val id = args.getOrNull(0)?.toIntOrNull() 
        ?: error("id expected")
    // ...
}
复制代码

在 API 设计中使用 null 吧,它是你和 Kotlin 的好朋友。没有理由去惧怕它,也没有理由使用 null object 模式或者包装器来处理它,更不用说异常了。在你的 API 中合理使用 null 会给你带来更可读,更安全的代码,而且远离样板代码。

深刻阅读

若是你喜欢这个主题,而且想了解更多关于语言设计的细节,那么能够考虑阅读这篇文章—— Dealing with absence of value

文章首发微信公众号: 秉心说 , 专一 Java 、 Android 原创知识分享,LeetCode 题解。

更多最新原创文章,扫码关注我吧!

相关文章
相关标签/搜索