使用Java 8 的 Optional 类进行优雅的判空

使用Java 8 的 Optional 类进行优雅的判空

前言

这篇是身为技术菜鸟的博主写的第一篇技术文章,新手上路,文笔粗糙,还请大家多多担待。

之所以写这篇文章,起因是我看完《Java 8 in Action》 也有一段时间了,在日常工作中,也经常使用 Java 8 的语法,就打算写下有关 Java 8 的一系列文章,用于自我复习和总结,加深自己对 Java 8的理解和使用,好了,言归正传,下面就让我们进入主题吧。

你是否曾经被 NullPointerException 异常折磨的苦不堪言?你是否曾为复杂的POJO 类写非空判断,为那多个 if xxx != null 或多层嵌套 if xxx != null 的代码感到烦恼心累?

呐,程序猿们,我们是时候用Java 8 的 Optional 类进行优雅的判空啦,当然,如果你用的是 Java 8 的话(:з」∠)

Optional 是什么?

java.util.Optional 类是 Java 8 引入的一个新的类,是一个容器,可以保存类型为T的值,也可以保存null值,它提供了许多有用的方法,使得我们可以不用显示进行空值检查。

Optional 的类方法

方法 描述
static Optional empty() 返回一个空的 Optional 实例
static Optional of(T value) 返回一个包含指定 value 的 Optional,如果 value 为空,则抛出 NullPointerException
static Optional ofNullable(T value) 将指定的值用 Optional 封装之后返回,如果 value 为null,则返回一个空的 Optional 对象
T get() 如果 Optional 包含有值,则返回该值,否则抛出 NoSuchElementException 异常
boolean isPresent() 如果 Optional 有值,则返回 true,否则返回 false
void ifPresent(Consumer<? super T> action) 如果 Optional 有值,则使用该值调用 consumer,否则不做任何事情
Optional filter(Predicate<? super T> predicate) 如果 Optional 有值,且该值与给定的 predicate 匹配,则返回 该 Optional,否则返回一个空的 Optional
Optional map(Function<? super T, ? extends U> mapper) 如果 Optional 有值存在,就对该值执行提供的mapping 函数调用
Optional flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) 如果 Optional 有值存在,就对该值执行提供的mapping 函数调用,返回一个Optional 类型的值,否则就返回一个空的Optional 对象
T orElse(T other) 如果 Optional 有值,则将其返回,否则返回指定 other 值
T orElseGet(Supplier<? extends T> supplier) 如果 Optional 有值则将其返回,否则返回一个由指定的Supplier 接口生成的值
T orElseThrow() 如果 Optional 有值,则将其返回,否则抛出 NoSuchElementException 异常
T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X 如果 Optional 有值则将其返回,否则抛出一个由指定的 Supplier 接口生成的异常
Stream stream() 如果 Optional 有值,则返回一个包含 Optional 的 Stream,否则返回一个空的 Stream

Optional 的应用实例

上面基本上列出了 Optional 类的所有方法,下面挑几个常用的方法进行介绍:

1
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
public class OptionalTest {

@Data
@AllArgsConstructor
@NoArgsConstructor
private static class Basket {
private Apple apple;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
private static class Apple {
private String color;

private Double weight;
}

public static void main(String[] args) {
Apple apple = new Apple("red", 20.00);
Basket basket = new Basket(apple);
try {
// 返回一个包含指定 apple 的 Optional,如果 apple 为空,则抛出 NullPointerException
Optional<Apple> optionalApple1 = Optional.of(apple);
} catch (NullPointerException e) {
e.printStackTrace();
}

// 将 apple 用 Optional 封装之后返回,如果 apple 为null,则返回一个空的 Optional 对象
// 推荐采用这种方式
Optional<Apple> optionalApple2 = Optional.ofNullable(apple);

try {
// 如果 optionalApple 包含有值,则返回该值,否则抛出 NoSuchElementException 异常
Apple apple1 = optionalApple2.get();
} catch (NoSuchElementException e) {
e.printStackTrace();
}

// 如果不想get()抛出异常,可以使用 orElse() 方法
// 如果 optionalApple2 有值,则将其返回,否则返回指定 null 值
Apple apple1 = optionalApple2.orElse(null);

// 如果 optionalApple2 有值存在,就对该值执行提供的mapping 函数调用
Optional<Double> weightOptional = optionalApple2.map(Apple::getWeight);

// 可以与 get() 一起使用,或者与 orElse() 一起使用
Double weight1 = optionalApple2.map(Apple::getWeight).get();
Double weight2 = optionalApple2.map(Apple::getWeight).orElse(00.00);

// 从 Basket 中 获取 Apple 的 weight
Double appleWeight1 = Optional.ofNullable(basket)
.map(Basket::getApple)
.map(Apple::getWeight)
.orElse(00.00);

// 使用 orElseThrow
Double appleWeight2 = Optional.ofNullable(basket)
.map(Basket::getApple)
.map(Apple::getWeight)
.orElseThrow(NullPointerException::new);
}
}

使用 Optional 进行优雅判空

还是用上面列举的类,如果不用使用 Optional,想要获取 Basket 类内的 Apple 类的 weight 值,得这样写:

1
2
3
4
5
6
7
8
9
10
11
12
public double getAppleWeight(Basket basket){
if (basket != null){
Apple apple = basket.getApple();
if (apple != null){
Double weight = apple.getWeight();
if (weight != null){
return weight;
}
}
}
return 0.0;
}

看看这个 if 嵌套,emm…,这还是较为简单的 pojo 类,如果是复杂的,简直不堪入目啊。
而在 Optional 的加持下,只需这样写:

1
2
3
4
5
6
public double getAppleWeight(Basket basket){
return Optional.ofNullable(basket)
.map(Basket::getApple)
.map(Apple::getWeight)
.orElse(0.0);
}

瞬间代码就清爽了,嘻嘻,人也跟着欢快起来了。

当然 Optional 不止能进行优雅的判空,还可以利用 Optional 的其它的API,进行一些神奇的操作,比如使用 filter 进行参数校验之类的。

参考:

  1. 《Java 8 in Action》
  2. Java8 如何正确使用 Optional