Java8 那些事儿(二):Optional 类解决空指针异常

前言

空指针异常是导致 Java 应用程序失败的最常见原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到 Google Guava 的启发,Optional 类已经成为 Java8 类库的一部分。Optional 实际上是个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 范例

构造方式

Optional 的三种构造方式:Optional.of(obj),Optional.ofNullable(obj) 和 Optional.empty()。

  • Optional.of(),依据一个非空值创建 Optional。
1
2
// 如果 car 是一个 null, 这段代码会立即抛出 NullPointException, 而不是等到试图访问 car 的属性值时才返回一个错误.
Optional<Integer> optCar = Optional.of(car);
  • Optional.ofNullable(),可接受 null 的 Optional(实现序列化的域模型时使用)。
1
2
// 创建一个允许 null 值的 Optional 对象, 如果 car 是 null, 那么得到的 Optional 对象就是个空对象.
Optional<Integer> optCar = Optional.ofNullable(car);
  • Optional.empty(),所有 null 包装成的 Optional 对象。
1
2
// 声明一个空的 Optional
Optional<Integer> empty = Optional.empty();

isPresent()/ifPresent(Consumer consumer)

isPresent(),判断值是否存在。

1
2
3
4
5
6
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

// isPresent 判断值是否存在
System.out.println(optional1.isPresent() == true);
System.out.println(optional2.isPresent() == false);

ifPresent(Consumer consumer):如果 option 对象保存的值不是 null,则调用 consumer 对象,否则不调用。

1
2
3
4
5
6
7
8
9
10
// 如果不是 null, 调用 Consumer; 如果 null, 不调用 Consumer
optional1.ifPresent(new Consumer<Integer>() {
@Override
public void accept(Integer t) {
System.out.println("value is" + t);
}
});

// Lambda 表达式写法
optional1.ifPresent(t -> System.out.println("value is" + t));

orElse(value)/orElseGet(Supplier supplier)

orElse(value):如果 optional 对象保存的值不是 null,则返回原来的值,否则返回 value。

1
2
3
4
5
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElse(1000) == 1);// true
System.out.println(optional2.orElse(1000) == 1000);// true

orElseGet(Supplier supplier):功能与 orElse 一样,只不过 orElseGet 参数是一个对象,即无则由函数来产生。

1
2
3
4
5
6
7
8
9
10
11
12
Optional<Integer> optional1 = Optional.ofNullable(1);
Optional<Integer> optional2 = Optional.ofNullable(null);

System.out.println(optional1.orElseGet(() -> {
return 1000;
}) == 1);
// true

System.out.println(optional2.orElseGet(() -> {
return 1000;
}) == 1000);
// true

orElseThrow()

orElseThrow():值不存在则抛出异常,存在则什么不做,有点类似 Guava 的 Precoditions。

1
2
3
4
5
Optional<Integer> optional = Optional.ofNullable(null);

optional.orElseThrow(() -> {
throw new NullPointerException();
});

filter(Predicate)

filter(Predicate):判断 Optional 对象中保存的值是否满足 Predicate,并返回新的 Optional。

1
2
3
Optional<Integer> optional = Optional.ofNullable(1);

Optional<Integer> filter = optional.filter((a) -> a == 1);

map(Function)/flatMap(Function)

map(Function) 如果有值,则对其执行调用 mapping 函数得到返回值,否则返回空 Optional。如果返回值不为 null,则创建包含 mapping 返回值的 Optional 作为 map 方法返回值,否则返回空 Optional。

1
2
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));

flatMap(Function) 如果有值,为其执行 mapping 函数返回 Optional 类型返回值,否则返回空 Optional。flatMap 与 map(Funtion)方法类似,区别在于 flatMap 中的 mapper 返回值必须是 Optional。调用结束时,flatMap 不会对结果用 Optional 封装。[P.S. 参考博文 [3]:Cascading Optional Objects Using the flatMap Method]

1
2
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));

Optional 类的链式方法

错误示范 / 正确示范

  • 示范(一)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ** 错误示范
Optional<User> user = ......
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}
// 二者实质上是没有任何分别
User user = .....
if (user != null) {
return user.getOrders();
} else {
return Collections.emptyList();
}

// ** 正确示范
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())

// map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
  • 示范(二)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// ** 错误示范
String version = "UNKNOWN";
if(computer != null){
Soundcard soundcard = computer.getSoundcard();
if(soundcard != null){
USB usb = soundcard.getUSB();
if(usb != null){
version = usb.getVersion();
}
}
}

// ** 正确示范
String version = computer.map(Computer::getSoundcard)
.map(Soundcard::getUSB)
.map(USB::getVersion)
.orElse("UNKNOWN");

参考博文

[1]. Java 8 Optional 类深度解析
[2]. 使用 Java8 Optional 的正确姿势
[3]. Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!


Java8 那些事儿系列


谢谢你长得那么好看,还打赏我!😘
0%