距离上一次更新该文章已经过了 714 天,文章所描述的內容可能已经发生变化,请留意。
首先,Optional是一个容器,用于放置可能为空的值,它可以合理而优雅的处理null。
不合适的使用方式
记录一些API
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
| 1. empty() 返回一个Optional容器对象,而不是 null。建议常用⭐⭐⭐⭐
2. of(T value) 创建一个Optional对象,如果 value 是 null,则抛出 NPE。不建议用⭐⭐
3. ofNullable(T value) 同上,创建一个Optional对象,但 value 为空时返回Optional.empty()。推荐使用⭐⭐⭐⭐⭐
4. get() 返回Optional中包装的值,在判空之前,千万不要直接使用!尽量别用!⭐
5. orElse(T other) 同样是返回Optional中包装的值,但不同的是当取不到值时,返回你指定的 default。看似很好,但不建议用⭐⭐
6. orElseGet(Supplier<? extends T> other) 同样是返回Optional中包装的值,取不到值时,返回你指定的 default。看似和 5 一样,但推荐使用⭐⭐⭐⭐⭐
7. orElseThrow(Supplier<? extends X> exceptionSupplier) 返回Optional中包装的值,取不到值时抛出指定的异常。阻塞性业务场景推荐使用⭐⭐⭐⭐
8. isPresent() 判断Optional中是否有值,返回 boolean,某些情况下很有用,但尽量不要用在 if 判断体中。可以用⭐⭐⭐
9. ifPresent(Consumer<? super T> consumer) 判断Optional中是否有值,有值则执行 consumer,否则什么都不干。日常情况下请使用这个⭐⭐⭐⭐
|
最佳实践
业务上需要空值时
1 2 3 4 5 6 7
| public Optional<User> getUser(String name) { if (StringUtil.isNotEmpty(name)) { return RemoteService.getUser(name); } return Optional.empty(); }
|
使用 orElseGet()
1
| 获取 value 有三种方式:get() orElse() orElseGet()。这里推荐在需要用到的地方只用 orElseGet()。
|
首先,get()不能直接使用,需要结合判空使用。这和!=null其实没多大区别,只是在表达和抽象上有所改善。
其次,为什么不推荐orElse()呢?因为orElse()无论如何都会执行括号中的内容, orElseGet()只在主体 value 是空时执行,下面看个例子:
1 2 3
| public String getName() { System.out.print("method called"); }
|
1 2
| String name1 = Optional.of("String").orElse(getName()); String name2 = Optional.of("String").orElseGet(() -> getName());
|
如果上面的例子getName()方法是一个远程调用,或者涉及大量的文件 IO,代价可想而知。
但 orElse()就一无是处吗?并不是。orElseGet()需要构建一个Supplier,如果只是简单的返回一个静态资源、字符串等等,直接返回静态资源即可。
1 2 3 4 5 6 7 8 9 10 11 12
| public static final String USER_STATUS = "UNKNOWN"; ... public String findUserStatus(long id) { Optional<String> status = ... ; return status.orElse(USER_STATUS); }
public String findUserStatus(long id) { Optional<String> status = ... ; return status.orElse("UNKNOWN"); }
|
使用 orElseThrow()
1 2 3 4 5 6
| 这个针对阻塞性的业务场景比较合适,例如没有从上游获取到用户信息,下面的所有操作都无法进行,那此时就应该抛出异常。正常的写法是先判空,再手动 throw 异常,现在可以集成为一行:
public String findUser(long id) { Optional<User> user = remoteService.getUserById(id) ; return user.orElseThrow(IllegalStateException::new); }
|
不为空则执行时,使用 ifPresent()
1 2 3 4 5 6 7 8
| 这点没有性能上的优势,但可以使代码更简洁:
if (status.isPresent()) { System.out.println("Status: " + status.get()); }
status.ifPresent(System.out::println);
|
不要滥用
1 2 3 4 5 6 7 8 9 10 11 12 13
|
public String fetchStatus() { String status = getStatus() ; return Optional.ofNullable(status).orElse("PENDING"); }
public String fetchStatus() { String status = ... ; return status == null ? "PENDING" : status; }
|