简介
Optional
是 Java 8 新增的一个工具类。它是一个容器,用以包装一个值对象。
使用 Optional
可以大大消除充斥于代码中的“判空”操作,使代码更加优雅。
创建实例
如果查看 Optional
的源码可以发现,它只有一个私有的构造器。因此,不能通过构造器创建其对象,
只能通过 3 个静态工厂方法来创建。
1 | Optional<T> empty() |
empty()
用于创建一个“空”的 Optional
对象,它不包含任何值。
值得注意的是,empty()
返回的是 Optional
内部缓存的同一个对象,我们暂称之为“缓存空对象”。
更进一步,Optional
方法所返回的“空对象”通常也都是这个“缓存空对象“。
of()
用于创建一个“非空”的 Optional
对象,因此,如果传入 null
将会抛出“空指针”。
ofNullable()
顾名思义,它可以创建一个任意的 Optional
对象,即使它是“空”的。但是,如果传入 null
参数则还是会返回“缓存空对象”,而不会另创建一个“空对象”。
获取值
Optional
包装了一个值,我们最终的目的必定是使用这个值,因此,总是需要提供获取该值的方法:
1 | var value = optional.get(); |
需要注意的是,直接调用 get()
方法是有风险的。如果 optional
是一个“空对象”,那么,调 get()
方法将抛出一个 NoSuchElementException
。
常规判断
有时我们需要根据值是否为“空”要决定如何使用该值,所以,需要事先判断:
1 | optional.isEmpty(); |
函数式条件选择
“判断-行为”模式是命令式编程方式,如今 Java 逐渐加入了很多函数式编程能力,而 Optional
就有很多这样方法。
对于“值”而言,最常见的一种处理行为是:如果不为空就取其值,否则取另一个值(通常意义上的“默认值”)。
为此,Optional
提供了以下 3 个方法来实现该功能:
1 | T orElse(T other) |
最简单直接的就是 orElse()
,调用该方法的 Optional
对象包装的“非空值”时,直接返回该“值”,否则返回参数给定的“默认值”。
进一步地,如果这个“默认值”不是现成的,需要一些计算呢?更复杂一点,如果这些计算很耗时,想要“延迟计算”呢?那么,就需要使用 orElseGet()
。
再进一步,如果想要“默认值”也以 Optional
对象的形式返回呢?使用 or()
方法。这可能常见于“链式编程”中。
orElseGet()
和 or()
方法的参数都是 Supplier
,这要求使用者应当了解 lambda 表达式等知识点,这里就不展开讨论了。给一些示例:
1 | Optional.empty().orElse("default"); // default |
在某些情况下,不应得到一个“默认值”,而是应该抛出异常,报告错误。这就得用到以下两个方法了:
1 | T orElseThrow() |
orElseThrow()
方法在“空值”情况下会抛出 NoSuchElementException
。
这与
get()
方法的行为一模一样,不是么?是的,甚至它们的源码都是一样的。那为什么要写两个方法?仔细看 API 文档,
orElseThrow()
是 Java 10 才加入的。get()
方法的注释说明了其最佳推荐替代方法就是orElseThrow()
。为什么要加一个方法替代呢?笔者推测,这里应该是出于语义的考量。
get
字面上就是“取值”的意思,在现实语义中,如果取的值不存在,通常我们说“不存在”,而不是说“这是一个错误”。因此,get()
的行为与现实语义有差异,存在误用的可能。而orElseThrow()
字面就强调了“抛出异常”。编程中,应尽量使用
orElseThrow()
而非get()
。
“值”的另一种处理行为是:如果不为空就以某种方式处理,或者为空则以另一种方式处理。这对应以下两个方法:
1 | void ifPresent(Consumer<? super T> action) |
显然,ifPresent()
只处理值存在的情况,而 ifPresentOrElse()
不论值是否存在都提供了处理方式。
1 | Optional.empty().ifPresent(value -> System.out.println(value)); // 无输出 |
数据处理
Optional
还提供了数据映射和过滤的能力,由以下 3 个方法提供:
1 | <U> Optional<U> map(Function<? super T, ? extends U> mapper) |
map()
和 flatMap()
区别在于,前者会将值自动包装为 Optional
而后者需要自行包装返回。而 filter()
用于过滤 Optional
。
1 | Optional.empty().map(e -> "value"); // Optional.empty |
不可避免的空指针
Optional
可以更为“优雅”地处理“空值”,而不用频繁判断。但是,这并不代表其所有方法都不会抛出空指针异常,以下调用还是会导致空指针的:
1 | // empty 代表一个“空”Optional |
异常可不止空指针
关于异常,还值得注意的是,它可能还会抛出 NoSuchElementException
。该异常意味着,试图获取一个“空对象的值”。显然,只有 get()
和 orElseThrow()
这两个取值方法可能导致该异常。
1 | // NoSuchElementException |
小结
使用 Optional
的优点之一就是不用关注空值,而只用关注处理逻辑,因此,代码具有一致性。
当然,Optional
可以进行链式编程,进行一系列的值处理。
总之,Optional
为值处理提供了“确定性”,只要正确使用其“可信”的方法,其结果就是稳定可期的。
Optional
可以很优雅地获取带默认值的数据,比如:String value = Optional.ofNullable(settingValue).orElse(defaultValue);
。
方法列表
1 | static <T> Optional<T> empty() |