Optional 的不正确实践

首先,不要直接拿来做if-else的判定条件,这肯定是错的:

1
2
3
4
5
6
Optional<User> user = ...... 
if (user.isPresent()) {
return user.getOrders();
} else {
return Collections.emptyList();
}

而且get()要配合isPresent()才安全。

其次,尽量不要用 Optional 拿来做成员变量,特别是 pojo 的成员变量,这很容易让读 pojo 的框架出问题。

再次,不要拿来做方法参数,因为很可能写成 if (user.isPresent()) {}式的代码。

最后,Optional 的关注点在它的 value 的后续处理身上,如果这个 value 只是一个 flag,还是要乖乖地用if-else;如果这个 value 被用在多路返回里,也不能使用 Optional。

正确的用法

选对构造器

Optional.of(T value)

如果需要断言值不为空,使用这个构造器。

该方法通过一个非 null 的 value 来构造一个 Optional,返回的 Optional 包含了 value 这个值。对于该方法,传入的参数一定不能为 null,否则便会抛出 NullPointerException。

Optional.ofNullable(T value)

如果这个值可以为空,使用这个构造器。

该方法和 of 方法的区别在于,传入的参数可以为 null , 但是前面 javadoc 不是说 Optional 只能包含非 null 值吗?原来该方法会判断传入的参数是否为 null,如果为 null 的话,返回的就是 Optional.empty()。

Optional.empty()

最好搭配其他构造器使用这个方法。

该方法用来构造一个空的 Optional,即该 Optional 中不包含值,其实底层实现还是 如果 Optional 中的 value 为 null 则该 Optional 为不包含值的状态,然后在 API 层面将 Optional 表现的不能包含 null 值,使得 Optional 只存在 包含值 和 不包含值 两种状态。

选对返回值

要用orElseorElseGetmap + collect来打扁结构,注意这几个方法是休止方法。很适合拿来配合 supplier 一起使用。

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
// 存在即返回, 无则提供默认值
return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER);

// 存在即返回, 无则由函数来产生
return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

// map 函数隆重登场,map 和 orElse 是要相对应使用的。
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
// map 是可能无限级联的
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
// flatMap 方法与 map 方法的区别在于,map 方法参数中的函数 mapper 输出的是值,然后 map 方法会使用 Optional.ofNullable 将其包装为 Optional;而 flatMap 要求参数中的函数 mapper 输出的就是 Optional。

Optional<String> username = Optional
.ofNullable(getUserById(id))
.flatMap(user -> Optional.of(user.getUsername()))
.flatMap(name -> Optional.of(name.toLowerCase()));

System.out.println("Username is: " + username.orElse("Unknown"));

选对消费者

ifPresent可以用来接受闭包,实现 cps 风格,很适合搭配 consumer 一起使用。

1
2
3
4
5
6
7
// 存在才对它做点什么
user.ifPresent(System.out::println);

//而不要下边那样
if (user.isPresent()) {
System.out.println(user.get());
}

Java 9 对Optional的增强

or方法

or 方法的作用是,如果一个 Optional 包含值,则返回自己;否则返回由参数 supplier 获得的 Optional

ifPresentOrElse

ifPresentOrElse 方法的用途是,如果一个 Optional 包含值,则对其包含的值调用函数 action,即 action.accept(value),这与 ifPresent 一致;与 ifPresent 方法的区别在于,ifPresentOrElse 还有第二个参数 emptyAction —— 如果 Optional 不包含值,那么 ifPresentOrElse 便会调用 emptyAction,即 emptyAction.run()

stream

stream 方法的作用就是将 Optional 转为一个 Stream,如果该 Optional 中包含值,那么就返回包含这个值的 Stream;否则返回一个空的 Stream(Stream.empty())。
举个例子,在 Java8,我们会写下面的代码:

1
2
3
4
5
6
7
8
// 此处 getUserById 返回的是 Optional<User>
public List<User> getUsers(Collection<Integer> userIds) {
return userIds.stream()
.map(this::getUserById) // 获得 Stream<Optional<User>>
.filter(Optional::isPresent)// 去掉不包含值的 Optional
.map(Optional::get)
.collect(Collectors.toList());
}

而有了 Optional.stream(),我们就可以将其简化为

1
2
3
4
5
6
public List<User> getUsers(Collection<Integer> userIds) {
return userIds.stream()
.map(this::getUserById) // 获得 Stream<Optional<User>>
.flatMap(Optional::stream) // Stream 的 flatMap 方法将多个流合成一个流
.collect(Collectors.toList());
}

参考文献:

  1. 《使用 Java8 Optional 的正确姿势》
    2.《Java高级(三):Optional的巧用》