# 简介

Optional 类的引入很好的解决空指针异常。它是一个可以为 null 的容器对象, Optional 本身没有引入新技术,你可以把它看作对象的包装类,通过将对象封装在 Optional 中,可以省去大量空指针检查。

相关的方法可以快速阅读菜鸟教程。这里大致提一些相关的行为:

  • 获取 Optional 里的值,如果该值为空,返回默认值(可以指定默认值,有点像 HashMap 中的 getOrDefault
  • 检测 Optional 里的值,( ifPresent 方法接收 Consumer 接口,来对值进行操作)
  • 构造器私有,通过其他方法(静态)检测要构造对象所传入的对象是否为 null ,来选择调用构造器。

# 入门

举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car ,当变量存在时, Optional 类只是对 Car 的简单封装。变量不存在时,会使用 Optional.empty() 方法返回空的 Optional 对象。如下所示:

image-20220819112640846

但是 null 引用和 Optional.empty() 有什么本质区别?从语义上,它们可以当成一回事儿,但实际上差别非常大:如果尝试解引用一个 null ,一定会触发 NullPointerException ,不过使用 Optional.empty() 是一个有效对象。

# 创建对象

这里有 empty(),of(),ofNullable() 三个静态方法,都可以用来创建 Optional 对象,再次强调, Optional 的构造器是私有的。

public static<T> Optional<T> empty() {
        @SuppressWarnings("unchecked")
        Optional<T> t = (Optional<T>) EMPTY;
        return t;
    }
	
	public static <T> Optional<T> of(T value) {
        return new Optional<>(value);
    }
	
	public static <T> Optional<T> ofNullable(T value) {
        return value == null ? empty() : of(value);
    }

这里要说明的是, Optional 有一个 EMPTY 的静态属性。

private static final Optional<?> EMPTY = new Optional<>();

Optional 是不对外提供对 value==null 时修改 value 的方法的,因为 ifPresent 会进行 null 值检测

public void ifPresent(Consumer<? super T> consumer) {
        if (value != null)
            consumer.accept(value);
    }

所以一个空的 Optional 就永远不会被加入新的值(因为无法修改),这也就意味着,所有的空的 Optional 都可以指向同一个空的 Optional ,这就是 EMPTY

# 其他方法

# map

源码:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));
        }
    }

它和 ifPresent 的区别在于, map 接收的时 Function 接口,存在返回值, map 会将 apply 的返回值封装在一个新的 Optional 中。

//map 方法执行传入的 lambda 表达式参数对 Optional 实例的值进行修改。
// 为 lambda 表达式的返回值创建新的 Optional 实例作为 map 方法的返回值。
Optional<String> name = Optional.of("abc");
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));
// 输出:ABC

# flatMap

源码:

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));
        }
    }

可以看到,它和 map 的差别就是返回语句, flatmap 只是对 apply 函数返回值进行空值判断,而不将它封装在 Optional 中。但是,从函数定义可以看到, flatMap 其实返回的还是一个 Optional ,所以 mapper.apply 必须返回 Optional

upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));// 输出 SANAULLA

# fliter

filter 方法通过传入限定条件对 Optional 实例的值进行过滤。如果有值并且满足断言条件返回包含该值的 Optional ,否则返回空 Optional 。要加入判定条件,从这里可以知道, fliter 应该实现 Predicate 接口(断定型接口)。

public Optional<T> filter(Predicate<? super T> predicate) {
        Objects.requireNonNull(predicate);
        if (!isPresent()) // 如果为空
            return this;
        else
            return predicate.test(value) ? this : empty();
    }

# 参考

菜鸟教程:https://www.runoob.com/java/java8-optional-class.html

Java 全栈知识体系:https://pdai.tech/md/java/java8/java8-optional.html