# Optional 详解
# 简介
在 Java 8 之前,对于方法的返回值,没有一个很好的方式去表示返回的值是否为 null
,因此很多时候需要手动判断,甚至因为没有判断而发生 NPE 异常。于是 Java 8 专门新出了一个类 Optional,这个类的解释是:它是一个可以包含空或者非空值的容器,如果容器内的对象不为空,则 ifPresent
返回 true,否则返回 false。除此之外,Optional 还提供了其他的 API,方便对容器的对象进行获取、判断、转换、过滤等。
值得一提的是,Optional 类是用来表达方法返回结果可能为空的可能性。
# 基本使用
Optional 的代码比较简单,注释加代码只有几百行。
Optional 是一个 final 类型的类,没有实现 Serializable 接口,它只有一个私有的构造函数。
/**
* 用来表示值为 null 的实例
*/
private static final Optional<?> EMPTY = new Optional<>();
/**
* Optional 包含的值
*/
private final T value;
/**
* 私有构造函数
*/
private Optional() {
this.value = null;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 创建
Optional 有三种方法可以获得实例化对象。
/**
* 可以返回一个内置的,表示空的 Optional 实例
*/
public static <T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
/**
* 根据传入的 value 创建一个 Optional 实例
* 如果 value 为 null 则会抛出 NPE
*/
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
/**
* 根据传入的 value 创建一个 Optional 实例
* 如果 value 为 null 则返回一个表示空的实例
*/
public static <T> Optional<T> ofNullable(T value) {
return value == null ? empty() : of(value);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 判断
Optional 提供了 API 用于判断值是否存在,或者在值存在的情况下执行一些操作。
/**
* 如果 value 不为 null,则返回 true,否则返回 false
*/
public boolean isPresent() {
return value != null;
}
/**
* 如果值存在,则会调用给定的 action 动作
*/
public void ifPresent(Consumer<? super T> action) {
if (value != null) {
action.accept(value);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 获取
Optional 提供了 API 用于获取内部的值,或者在值不存在的情况下,返回另一个默认值或抛出异常。
/**
* 如果值存在,则返回值,否则会判处 NoSuchElementException 异常。
* 因此对于 Optional 的返回值,一定要先判断值是否存在,类似于编译时
* 检查异常,一定要你去处理
*/
public T get() {
if (value == null) {
throw new NoSuchElementException("No value present");
}
return value;
}
/**
* 返回内部的值,否则返回一个默认值
*/
public T orElse(T other) {
return value != null ? value : other;
}
/**
* 返回内部值,如果内部值为空则执行 Supplier 实例的 get 方法返回默认值
* 只有在 value == null 时,才会执行 Supplier 实例的 get 方法
*/
public T orElseGet(Supplier<? extends T> other) {
return value != null ? value : other.get();
}
/**
* 返回内部值,如果内部值为空则抛出 Supplier 实例返回的异常
* <X extends Throwable> 表示 Supplier 实例返回的是 Throwable 的子类
*/
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
if (value != null) {
return value;
} else {
throw exceptionSupplier.get();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 转换
Optional 也提供类似 Stream 的 API,通过 map 或者 flatMap 方法,将值转换为另一种类型。
/**
* 如果 value 存在,则使用传入的 mapper 对象进行类型转换,
* 并且会将转换后的对象包装成 Optional 实例。
*
* 如果 value 不存在,则会返回一个空的 Optional 实例。
*/
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Optional.ofNullable(mapper.apply(value));
}
}
/**
* 作用同 map 方法,区别点是 flatMap 传入的 mapper 对象本身
* 就会返回一个 Optional 实例。
*/
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 过滤
filter 是用来对 Optional 内的值进行校验,类似于 if(checkValue(returnValue))
这样的做法。
/**
* 传入一个断言对象 predicate,如果 value 符合 predicate 的规则
* 的话,则将当前 Optional 对象返回,否则返回一个空的 Optional 实例
*/
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
2
3
4
5
6
7
8
9
10
11
# 最佳实践
# 不要滥用 Optional
需要对某个传入参数或者其他对象进行判空时,直接通过 if 来判断效率更高,通过 Optional.ofNullable 不仅降低可读性还产生一个无谓的 Optional 实例。
String str = Optional.ofNullable(param).orElse("default");
# 在 Java Bean 中慎用 Optional
不要使用 Optional 作为 Bean 的成员变量,如果想表达某个字段可能不存在,可以在 getter 的返回值上做处理。因为 Optional 没有实现 Serializable 接口,不可序列化。
public class User {
private String phoneNumber;
public Optional<String> getPhoneNumber() {
return this.phoneNumber;
}
}
2
3
4
5
6
7
# 避免将 Optional 作为方法的参数类型
当想表达对于一个方法来说,其参数值是可以为空的时候,不建议使用 Optional 作为参数类型,它不仅需要将参数多做一层包装,而且会让方法参数列表变得长,而且不利于阅读。
public void fun(Optional<String> p1, Optional<Integer> p2) {
}
2
3
可以考虑将方法拆成多个重载方法。
public void fun(String p1, Integer p2);
public void fun(String p1);
public void fun(Integer p2);
public void fun();
2
3
4
# 不要在使用 Optional 去包装集合
类似于 List、Map、Set 这些集合类,Collections 已经有对应的空值对象了。
Collections.emptyList();
Collections.emptySet();
Collections.emptyMap();
Stream.empty()
2
3
4
# 🌟不要给 Optional 对象赋值 null
当表达 Optional 为空时,请用 empty() 方法。
public Optional<User> getUserInfo() {
Optional<User> result = Optional.empty();
...
return result;
}
2
3
4
5
# 🌟确保值存在再调用 Optional 的 get 方法
从源码可以看出来,如果 value 为 null,调用 get 方法是会抛出异常的。而且 IDEA 也会识别并给出警告。
Optional 强迫开发者去关注返回值为空的情况,并且需要去检查返回值。
# 🌟熟悉 Optional 的 API
熟悉 Optional 的 API ,并在使用 Optional 的时候避免使用 if-else 这种方式,而是通过 Optional.orElse() Optional.orElseGet() Optional.ifPresent() 可以避免手写 if-else 语句,使代码更简洁。
# 使用 equals 而不是 == 比较 Optional
Optional 的 equals 方法已经实现了内部值比较。
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Optional)) {
return false;
}
Optional<?> other = (Optional<?>) obj;
return Objects.equals(value, other.value);
}
2
3
4
5
6
7
8
9
10
11
12
13
# 参考文章
- https://zhuanlan.zhihu.com/p/128481434