Java8 Optional
一句话介绍 Optional 类: 使用 JDK8 的 Optional 类来防止 NullPointerException(空指针异常) 问题.
一, 前言
在我们开放过程中, 碰到的异常中 NullPointerException 必然是排行第一的. 所以在平时编码中, 我们会时时的判断 null.
- public void saveCity(City city) {
- if (city != null) {
- String cityName = city.getCityName();
- if (cityName != null) {
- String code = cityDao.findCodeByName(cityName);
- city.setCode(code);
- cityDao.save(city);
- }
- }
- }
虽然上面代码变得更加安全, 但是过多嵌套 if 语句降低代码整体可读性, 提高复杂度. 我们可以优化下代码
- public void saveCity(City city) {
- if (city == null) {
- return;
- }
- String cityName = city.getCityName();
- if (cityName == null) {
- return;
- }
- String code = cityDao.findCodeByName(cityName);
- city.setCode(code);
- cityDao.save(city);
- }
这样还可以, 但我们通过 Optional 变的更简洁
- public void saveCity(City city) {
- // 就一行 city 不为空返回 城市名称 否则直接返回空
- Optional<String> roleOpt = Optional.ofNullable(city).map(City::getCityName);
- // 如果容器中 不为空
- if (roleOpt.isPresent()) {
- String code = cityDao.findCodeByName(roleOpt.get());
- city.setCode(code);
- cityDao.save(city);
- }
- }
这样, 我们仅需要对我们关心的做一次校验, 省却了前面的一系列的检验操作.
二, Optional API
概念 Optiona 本质是一个容器, 容器中存在为 null 或者不包含非 null 值的容器对象. 提供了一系列的方法供我们判断该容器里的对象是否存在.
1,JDK 源码
- /**
- * final 修饰代表不能被子类继承
- */
- public final class Optional<T> {
- /**
- * 创建一个空容器
- */
- private static final java.util.Optional<?> EMPTY = new java.util.Optional<>();
- /**
- * 传入的值
- */
- private final T value;
- /**
- * 构造函数私有化 说明不能被外部 new
- */
- private Optional() {
- this.value = null;
- }
- /**
- * 私有化构造函数
- */
- private Optional(T value) {
- this.value = Objects.requireNonNull(value);
- }
- /**
- * 获取空容器
- */
- public static <T> java.util.Optional<T> empty() {
- @SuppressWarnings("unchecked")
- java.util.Optional<T> t = (java.util.Optional<T>) EMPTY;
- return t;
- }
- /**
- * 传入的对象不能为空 否则抛异常
- */
- public static <T> java.util.Optional<T> of(T value) {
- return new java.util.Optional<>(value);
- }
- /**
- * 传入的对象可以为空
- */
- public static <T> java.util.Optional<T> ofNullable(T value) {
- return value == null ? empty() : of(value);
- }
- /**
- * 获取容器对象的方法 注意 如果用这个方法则代表容器中一定有对象, 否则抛异常
- */
- public T get() {
- if (value == null) {
- throw new NoSuchElementException("No value present");
- }
- return value;
- }
- /**
- * 判断容器对象是否为空
- */
- public boolean isPresent() {
- return value != null;
- }
- /**
- * 如果容器对象为空 则返回当前对象
- */
- public T orElse(T other) {
- return value != null ? value : other;
- }
- //========== 有关下面这几个 JDK8 自带的函数式接口的作用, 上一篇博客有详细说明, 这里就不多说了.
- /**
- * 传入 Consumer 编程式接口参数
- */
- public void ifPresent(Consumer<? super T> consumer) {
- if (value != null)
- consumer.accept(value);
- }
- /**
- * 传入 Predicate 编程式接口参数
- */
- public java.util.Optional<T> filter(Predicate<? super T> predicate) {
- Objects.requireNonNull(predicate);
- if (!isPresent())
- return this;
- else
- return predicate.test(value) ? this : empty();
- }
- /**
- * 传入 Function 编程式接口参数
- */
- public <U> java.util.Optional<U> map(Function<? super T, ? extends U> mapper) {
- Objects.requireNonNull(mapper);
- if (!isPresent())
- return empty();
- else {
- return java.util.Optional.ofNullable(mapper.apply(value));
- }
- }
- /**
- * 传入 Function 编程式接口参数
- */
- public <U> java.util.Optional<U> flatMap(Function<? super T, java.util.Optional<U>> mapper) {
- Objects.requireNonNull(mapper);
- if (!isPresent())
- return empty();
- else {
- return Objects.requireNonNull(mapper.apply(value));
- }
- }
- /**
- * 传入 Supplier 编程式接口参数
- */
- public T orElseGet(Supplier<? extends T> other) {
- return value != null ? value : other.get();
- }
- /**
- * 传入 Supplier 编程式接口参数
- */
- public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
- if (value != null) {
- return value;
- } else {
- throw exceptionSupplier.get();
- }
- }
- }
2, 创建 Optional 对象
通过上面源码可以看出, Optional 的构造函数都是私有化的, 无法直接 new 对象. 它这边提供了 3 个静态方法获取对象.
1, 创建一个一定是空的 Optional 容器
Optional<Car> optCar = Optional.empty();
2, 创建一个一定是非空值 Optional 容器 (传入的对象不可以为 null, 否则抛出 NullPointerException)
Optional<Car> optUser = Optional.of(user);
3, 创建一个可能是空也可能不为空的 Optional 容器 (传入的对象可以为 null)
Optional<Car> optUser = Optional.ofNullable(user);
3, 总结常用方法
- ,isPresent() // 有值则返回 true
- ,get(): // 值存在时返回值, 否则抛出一个 NoSuchElement 异常 (所以调这个, 一般先判断上面方法返回是否为 true)
- ,orElse(T other) // 值存在时返回值, 否则返回一个默认值
- ,ifPresent(Consumer<T> block) // 会在值存在的时候执行给定的代码块
- ,orElseThrow(Supplier<? extends X> exceptionSupplier) // 与 get() 类似, 不同的是可以自定义异常类型
- ,orElseGet(Supplier<? extends T> other) //orElse 方法的延迟调用版, Supplier 方法只有在 Optional 对象不含值时才执行调用
- ,map/flatMap/filter // 与 Stream 中用法类似
三, 完整的示例
这里写一个针对以上 API 都涉及到的 Demo, 这个例子明白了, 那么 Optional 的使用也就都清楚了.
代码
- public class OptionalDemo {
- public static void main(String[] args) {
- //1, 创建 Optional 实例, 传入的对象不能为 null
- Optional<String> nameOptional = Optional.of("张三");
- //2, 创建 Optional 实例, 传入对象可以为 null, 也可以不 weinull
- Optional emptyOptional = Optional.ofNullable(null);
- //3,isPresent 方法用来检查 Optional 实例是否有值.
- if (nameOptional.isPresent()) {
- // 调用 get() 返回 Optional 值.
- System.out.println("1," + nameOptional.get());
- }
- try {
- //4, 在 Optional 实例上调用 get() 抛出 NoSuchElementException.
- System.out.println("2," + emptyOptional.get());
- } catch (NoSuchElementException ex) {
- System.out.println("3, 异常" + ex.getMessage());
- }
- //
- //5, 如果 Optional 值不为空, lambda 表达式会处理并在其上执行操作.(这里 x 代表就是 nameOptional 中的对象)
- nameOptional.ifPresent((x) -> {
- System.out.println("4, 字符串长度为:" + x.length());
- });
- //6, 如果有值 orElse 方法会返回 Optional 实例, 没值则返回当前值
- System.out.println("5,"+ emptyOptional.orElse("如果是空容器则返回李四"));
- System.out.println("6,"+nameOptional.orElse("如果是空容器则返回王五"));
- //7,orElseGet 与 orElse 类似, 区别在于传入的参数不同, 一个是直接传入对象, 这个是传入 Supplier 函数式接口
- System.out.println("7," + emptyOptional.orElseGet(() -> "李四"));
- System.out.println("8," + nameOptional.orElseGet(() -> "王五"));
- try {
- //8, 如果是空容器, 则可以抛出自定义异常.
- emptyOptional.orElseThrow(() -> new NullPointerException("空容器异常"));
- } catch (Throwable ex) {
- System.out.println("9," + ex.getMessage());
- }
- Optional<String> ageOptional = Optional.of("10");
- //9, 这里入参是 Function, 所以可以转换容器中的对象 好比将 String 对象转为 Integer 对象
- Optional<Integer> age = ageOptional.map((value) -> Integer.parseInt(value));
- /**
- * 10,flatMap 与 map(Funtion) 非常相似, 不同在于 map 返回可以将 String 对象转为 Integer 对象, 但 flatMap 转换后一定还是 String 对象
- */
- Optional<String> upperName = nameOptional.flatMap((value) -> Optional.of(value.toUpperCase()));
- //11,filter 方法检查 Optiona 值是否满足给定条件. 如果满足返回 Optional 实例值, 否则返回空 Optional.
- Optional<String> longName = nameOptional.filter((value) -> value.length()> 6);
- System.out.println("10," + longName.orElse("longName 容器的名字长度小于 6 位"));
- //12, 另一个示例, Optional 满足给定条件.
- Optional<String> anotherName = Optional.of("乌啦啦市长公主");
- Optional<String> shortName = anotherName.filter((value) -> value.length()> 6);
- System.out.println("11," + shortName.orElse("anotherName 容器的名字长度小于 6 位"));
- }
- }
运行结果
参考
1,JDK8 新特性之: Optional https://www.jianshu.com/p/47ba2853a125
2,Optional 类包含的方法介绍及其示例
你如果愿意有所作为, 就必须有始有终.(26)
来源: https://www.cnblogs.com/qdhxhz/p/12056745.html