Java-14- 发布了,再也不怕 -NullPointerException- 了!
它破
坏了 Java 的哲学。 Java 一直试图避免让程序员意识到指针的存在,唯一的例外是:null 指针。
它在 Java 的类型系统上开了个口子。 null 并不属于任何类型,这意味着它可以被赋值给任意引用类型的变量。这会导致问题, 原因是当这个变量被传递到系统中的另一个部分后,你将无法获知这个 null 变量最初赋值到底是什么类型。
其他语言如何解决 NPE 问题
我们知道,出了 Java 语言外,还有很多其他的面向对象语言,那么在其他的一些语言中,是如何解决 NPE 的问题的呢?
如在 Groovy 中使用安全导航操作符(Safe Navigation Operator)可以访问可能为 null 的变量:
def carInsuranceName = person?.car?.insurance?.name
Groovy 的安全导航操作符能够避免在访问这些可能为 null 引用的变量时发生 NullPointerException,在调用链中的变量遭遇 null 时将 null 引用沿着调用链传递下去,返回一个 null。
其实这个功能曾经考虑过增加一个类似的功能,但是后来又被舍弃了。
另外,在 Haskell 和 Scala 也有类似的替代品,如 Haskell 中的 Maybe 类型、Scala 中的 Option[T]。
在 Kotlin 中,其类型系统严格区分一个引用可以容纳 null 还是不能容纳。也就是说,一个变量是否可空必须显示声明,对于可空变量,在访问其成员时必须做空处理,否则无法编译通过:
var a: String = "abc"a = null // 编译错误
果允许为空,可以声明一个可空字符串,写作 String?:
var b: String? = "abc" //String? 表示该 String 类型变量可为空 b = null // 编译通过
看到这个?的时候,是不是发现和 Groovy 有点像?不过还是有一定区别的,这里就不展开了。
好了,书归正传,我们来看看作为一个 TOIBE 编程语言排行榜第一名的语言,Java 语言对于 NPE 做出了哪些努力!
Java 做了哪些努力
一直以来对于 null 和 NPE 的改进还是做出了一些努力的。
首先在 Java 8 中提供了 Optional,其实在 Java 8 推出之前,Google 的 Guava 库中就率先提供过 Optional 接口来使 null 快速失败。
Optional 在可能为 null 的对象上做了一层封装,Optional 对象包含了一些方法来显式地处理某个值是存在还是缺失,Optional 类强制你思考值不存在的情况,这样就能避免潜在的空指针异常。
但是设计 Optional 类的目的并不是完全取代 null,它的目的是设计更易理解的 API。通过 Optional,可以从方法签名就知道这个函数有可能返回一个缺失的值,这样强制你处理这些缺失值的情况。
关于 Optional 的用法,不是本文的重点,就不在这里详细介绍了,笔者在日常开发中经常结合 Stream 一起使用 Optional,还是比较好用的。
另外一个值得一提的就是最近(2020 年 03 月 17 日)发布的 JDK 14 中对于 NPE 有了一个增强。那就是 JEP 358: Helpful NullPointerExceptions
更有帮助的 NPE
JDK 14 中对于 NEP 有了一个增强,既然 NPE 暂时无法避免,那么就让他对开发者更有帮助一些。
?
每个 Java 开发人员都遇到过 NullPointerExceptions (NPEs)。由于 NPEs 可以发生在程序的几乎任何地方,试图捕获并从它们中恢复通常是不切实际的。因此,开发人员通常依赖于 JVM 来确定 NPE 实际发生时的来源。例如,假设在这段代码中出现了一个 NPE:
a.i = 99;
JVM 将打印出导致 NPE 的方法、文件名和行号:
Exception in thread "main" java.lang.NullPointerExceptionat Prog.main(Prog.java:5)
通过以上堆栈信息,开发人员可以定位到 a.i= 99 这一行,并推断出 a 一定是 null。
但是,对于更复杂的代码,如果不使用调试器,就不可能确定哪个变量是 null。假设在这段代码中出现了一个 NPE:
a.b.c.i = 99;
我们根本无法确定到底是 a 还是 b 或者是 c 在运行时是个 null 值。
但是,在 JDK14 以后,这种窘境就有解了。
在 JDK14 中,当运行期,试图对一个 bull 对象进行应用时,JVM 依然会抛出一个 NullPointerException (NPE),除此之外,还会通过通过分析程序的字节码指令,JVM 将精确地确定哪个变量是 null,并且在堆栈信息中明确的提示出来。
在 JDK 14 中,如果上文中的 a.i = 99 发生 NPE,将会打印如下堆栈:
Exception in thread "main" java.lang.NullPointerException:Cannot assign field "i" because "a" is nullat Prog.main(Prog.java:5)
评论