java8之避免空检查

在1965 年有人提出了这个计算机科学中最糟糕的错误,该错误比Windows 的反斜线更加丑陋、比 === 更加怪异、比PHP 更加常见、比CORS 更加不幸、比Java 泛型更加令人失望、比XMLHttpRequest 更加反复无常、比C 预处理器更加难以理解、比MongoDB 更加容易出现碎片问题、比UTF-16 更加令人遗憾。

如何防止Java中著名的NullPointerException问题?这是每个Java初学者迟早会问的关键问题之一。但是中级和专家程序员也绕不开时不时出现的这个错误。它是迄今为止Java和许多其他编程语言中最普遍的错误类型。

图领奖得主和Null-References的发明者Tony Hoare在2009年道歉并将这种错误成为自己的十亿美元错误。

“我把 Null 引用称为自己的十亿美元错误。它的发明是在1965 年,那时我用一个面向对象语言( ALGOL W )设计了第一个全面的引用类型系统。我的目的是确保所有引用的使用都是绝对安全的,编译器会自动进行检查。但是我未能抵御住诱惑,加入了Null引用,仅仅是因为实现起来非常容易。它导致了数不清的错误、漏洞和系统崩溃,可能在之后 40 年中造成了十亿美元的损失。近年来,大家开始使用各种程序分析程序,比如微软的 PREfix 和 PREfast 来检查引用,如果存在为非 Null 的风险时就提出警告。更新的程序设计语言比如 Spec# 已经引入了非 Null 引用的声明。这正是我在1965年拒绝的解决方案。” —— 《Null References: The Billion Dollar Mistake》托尼·霍尔(Tony Hoare),图灵奖得主

无论如何,我们必须处理它。那么我们可以做些什么来防止NullPointerExceptions呢?嗯,显而易见的答案是在所有地方添加空检查。由于空检查有点麻烦和痛苦,许多语言添加了特殊的语法来处理通过空合并运算符进行空检查- 在Groovy或Kotlin等语言中也称为elvis运算符。

不幸的是,Java没有提供这样的语法糖。但幸运的是,在Java Version 8中,事情变得更好了。这篇文章描述了一些技术如何通过利用Java 8的新功能(如lambda表达式)来防止编写不必要的空值检查。

提高Java 8中的空安全性

java8-stream教程一文中已经初步介绍了如何利用Java 8 的Optional类型来防止空值检查。

假设我们有一个像这样的嵌套类结构:

class Outer {
    Nested nested;
    Nested getNested() {
        return nested;
    }
}
class Nested {
    Inner inner;
    Inner getInner() {
        return inner;
    }
}
class Inner {
    String foo;
    String getFoo() {
        return foo;
    }
}

在此结构中解析深层嵌套路径可能有点尴尬。我们必须编写一堆空检查以确保不会引发NullPointerException

Outer outer = new Outer();
if (outer != null && outer.nested != null && outer.nested.inner != null) {
    System.out.println(outer.nested.inner.foo);
}

我们可以通过使用Java 8 Optional类型来消除所有这些空值检查。该map方法接受一个lambda表达式类型的Function,并自动将每个函数结果包装成一个Optional。这使我们能够以管道式的map进行连续操作。无效检测会在这种模式下自动进行。

Optional.of(new Outer())
    .map(Outer::getNested)
    .map(Nested::getInner)
    .map(Inner::getFoo)
    .ifPresent(System.out::println);

实现相同行为的另一种方法是使用Supplier函数来解析嵌套路径:

Outer obj = new Outer();
resolve(() -> obj.getNested().getInner().getFoo())
    .ifPresent(System.out::println);

调用obj.getNested().getInner().getFoo())可能会抛出一个NullPointerException。在这种情况下,将捕获异常并为该方法返回Optional.empty()。

public static <T> Optional<T> resolve(Supplier<T> resolver) {
    try {
        T result = resolver.get();
        return Optional.ofNullable(result);
    }
    catch (NullPointerException e) {
        return Optional.empty();
    }
}

请记住,两种解决方案可能都不如传统的空检查那样高效。在大多数情况下,这应该不是什么大问题。

翻译原文: https://winterbe.com/posts/2015/03/15/avoid-null-checks-in-java/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值