原创文章, 转载请私信. 订阅号 tastejava 学习加思考, 仔细品味java之美
Optional 是从 JDK1.8 开始提供的一个容器类, 主要用于避免空指针异常(NPE), 其提供的一系列方法配合 Lambda 表达式可以让代码更加清晰, 语义化, 以及避免了空指针异常的问题这里要注意是避免空指针异常, 而不是避免返回null.
关于 Optional 方法怎么使用的资料网络上也有很多了, 不过一些文章不是很清晰明确, 讲述 Api 之外并没有讲实际应用或者应用的意义在哪. 今天还是一样从源代码来分析 Optional 怎么使用, 以及其作用.
源码中注释已经说得比较清晰了, 我来摘录几句
Optional 是一个容器对象, 可能包含一个非空值也可能不包含. 包含值时isPresent方法会返回true, get 方法会返回包含的值.
Optional 是一个 value-based 类, 也就是说 Optional 包含的值相等时, 实例的hashCode方法返回值相等, equals 方法返回 true , 需要用到比较两个 Optional 实例是否相等的地方要注意, 如 == equals hashCode 或者 synchronization
JDK1.8 版本的 Optional 总共有17个方法, 除去两个私有的构造方法, 除去equals, hashCode, toString三个方法外, 我们来分析一下剩下12个 Optional 提供的方法.
先来看三个构造 Optional 实例的方法
public static<T> Optional<T> empty();
public static <T> Optional<T> of(T value);
public static <T> Optional<T> ofNullable(T value);
下面来看 Optional 中两个让人唾弃的方法, 不推荐使用
public boolean isPresent();
public T get();
为什么不推荐调用上面两个方法呢, 因为容易写出下方代码, 比原先判断 if null 的代码还脏.
Integer result;
if (optional.isPresent()) {
result = optional.get();
}
接下里我们来看 Optional 中真正有用的7个方法
public void ifPresent(Consumer<? super T> consumer);
public Optional<T> filter(Predicate<? super T> predicate)
public<U> Optional<U> map(Function<? super T, ? extends U> mapper);
// 如果 outer 包含的 nested 对象或者 nested 包含的 inner 对象为空
// 传统方式 每一个 get 方法都要进行空判断, 否则有可能抛出空指针异常
// 用 Optional map 实现链式调用, 而不需要判断 null
// 任何一层对象为 null 时, 变量获得的都是指定默认值 null
String name = Optional.of(outter).map(Outter::getNested)
.map(Nested::getInner)
.map(Inner::getName).orElse(null);
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper);
** 接下来看三个也比较重要的方法, orElse, orElseGet, orElseThrow**
10. 如果实例包含值, 那么返回这个值, 否则返回指定的默认值, 如null
public T orElse(T other);
public T orElseGet(Supplier<? extends T> other);
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier);
在使用 Repository 操作 Es 时, findById 通过 id 查询数据库记录, 返回结果为 Optional, 此时无需 if null 判断, 利用 orElseThrow , 不存在就抛出异常, 配合异常处理器渲染通用结果和错误提示信息返回给前端处理.
Optional<ApiManager> byId = apiManagerRepository
.findById(apiManagerDTO.getId());
ApiManager oldApiManager = byId
.orElseThrow(
() -> new SysException(SysCodeEnum.API_MANAGER_DOES_NOT_EXIST)
);