JDK8 Optional 应用场景与方式分析

原创文章, 转载请私信. 订阅号 tastejava 学习加思考, 仔细品味java之美

Optional 出现的意义

Optional 是从 JDK1.8 开始提供的一个容器类, 主要用于避免空指针异常(NPE), 其提供的一系列方法配合 Lambda 表达式可以让代码更加清晰, 语义化, 以及避免了空指针异常的问题这里要注意是避免空指针异常, 而不是避免返回null.

Optional 源码分析及使用

关于 Optional 方法怎么使用的资料网络上也有很多了, 不过一些文章不是很清晰明确, 讲述 Api 之外并没有讲实际应用或者应用的意义在哪. 今天还是一样从源代码来分析 Optional 怎么使用, 以及其作用.

什么是 Optional

源码中注释已经说得比较清晰了, 我来摘录几句

Optional 是一个容器对象, 可能包含一个非空值也可能不包含. 包含值时isPresent方法会返回true, get 方法会返回包含的值.
Optional 是一个 value-based 类, 也就是说 Optional 包含的值相等时, 实例的hashCode方法返回值相等, equals 方法返回 true , 需要用到比较两个 Optional 实例是否相等的地方要注意, 如 == equals hashCode 或者 synchronization

Optional 方法的使用

JDK1.8 版本的 Optional 总共有17个方法, 除去两个私有的构造方法, 除去equals, hashCode, toString三个方法外, 我们来分析一下剩下12个 Optional 提供的方法.
先来看三个构造 Optional 实例的方法

  1. empty 方法返回一个不包含值的 Optional 实例, 注意不保证返回的 empty 是单例, 不要用 == 比较.
    public static<T> Optional<T> empty();
  1. 返回一个 Optional 实例, 代表指定的非空值, 如果传入 null 会立刻抛出空指针异常
	public static <T> Optional<T> of(T value);
  1. 返回一个 Optional 实例, 如果指定非空值则实例包含非空值, 如果传入 null 返回不包含值的 empty
	public static <T> Optional<T> ofNullable(T value);

下面来看 Optional 中两个让人唾弃的方法, 不推荐使用

  1. isPresent 用来判断实例是否包含值, 如果不包含非空值返回 false, 否则返回 true
	public boolean isPresent();
  1. get 方法, 如果实例包含值则返回当前值, 否则抛出 NoSushElementException 异常.
	public T get();

为什么不推荐调用上面两个方法呢, 因为容易写出下方代码, 比原先判断 if null 的代码还脏.

		Integer result;
        if (optional.isPresent()) {
     
            result = optional.get();
        }

接下里我们来看 Optional 中真正有用的7个方法

  1. ifPresent 方法作用是当实例包含值时, 来执行传入的 Consumer, 比如调用一些其他方法.
	public void ifPresent(Consumer<? super T> consumer);
  1. filter 方法用于过滤不符合条件的值, 接收一个 Predicate 参数, 如果符合条件返回代表值的 Optional 实例, 否则返回 empty
	public Optional<T> filter(Predicate<? super T> predicate)
  1. map 方法是链式调用避免空指针的核心方法, 当实例包含值时, 对值执行传入的 Function 逻辑, 并返回一个代表结果值的新的 Optional 实例. 这也意味着返回的结果依旧可以继续调用 map 方法, 而不需要空指针判断.方法签名以及示例如下:
	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);
  1. flatMap 方法与 map 方法作用一致, 不过 flatMap 接收的参数 Function 要求返回一个 Optional 实例, 并且 flatMap 方法直接返回该结果, 而不对结果包装一层 Optional, 适用于 Optional 包含的值也是 Optional, 可以进行多层 Optional 的合并.
	public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper);

** 接下来看三个也比较重要的方法, orElse, orElseGet, orElseThrow**
10. 如果实例包含值, 那么返回这个值, 否则返回指定的默认值, 如null

	public T orElse(T other);
  1. 如果实例包含值, 返回这个值, 否则调用传入的 Supplier 参数生产一个值.
	public T orElseGet(Supplier<? extends T> other);
  1. 如果实例不包含值, 调用传入的 Supplier 参数, 生成一个异常实例并抛出.这个方法通常与全局异常处理器一起使用, 当参数或者其他情况获取不到值时, 抛出自定义异常, 由异常处理器处理成通用返回结果, 返回给前端.
	public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier);

实际开发中 Optional 的应用

在使用 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)
		);

你可能感兴趣的:(Java,JDK8,Java1.8,Optional)