Optional详解

  Optional类的出现是为了更好的处理Java中的空对象,因为对空对象进行调用,会抛出NullPointerException。Java语言的设计其实是希望开发者对指针无感,然而这却是一个例外,可以说有些违背初衷。
对于那些可能为空,也可能非空的对象,使用Optional对其进行包装就可以屏蔽空指针异常。但是Optional也不宜滥用,本来不应为空却为空的对象,使用Optional对其进行包装,虽然屏蔽了空指针异常,却掩盖了真实的问题,以后排查会更难。
 
  Optional类只有两个属性,分别是value和EMPTY。value是将要被Optional包装的值,而EMPTY则是被Optional包装的null对象。
 
  Optional类中设计了六类功能,分别为:
  1. 创建Optional对象
  2. 判断Optional中的值是否为空
  3. 读取Optional中的值
  4. 操作Optional中的值
  5. Optional中的值为null怎么办
  6. 过滤Optional对象
 
一、创建Optional对象
 
  Optional的构造器已经被私有化开发者不能通过new去创建。但是它提供了三个静态方法来创建Optional对象,分别是:
  • empty : 创建一个值为null的Optional对象。一般很少用。
  • of : 创建一个非空值的Optional对象。值一定非空,否则将抛出NullPointerException异常。
  • ofNullable : 创建一个可能为空值的Optional对象。
Optional<String> empty = Optional.empty();  // Optional.empty
Optional<String> name = Optional.of("孙悟空");  // Optional[孙悟空]

String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> result = Optional.ofNullable(arr[index]);
String maybe = person.map(p -> "原来真的" + p).orElse("空的");  // 如果person不为空,返回"原来真的非空",否则返回"空的"

 

二、判断Optional中的值是否为空

  Optional类提供了一个isPresent()方法来判断值是否非空,如果非空返回true,否则返回false。

String[] arr = {null, "非空"};
int index = new Random().nextInt(2);
Optional
<String> person = Optional.ofNullable(arr[index]); if (person.isPresent()) {   System.out.println(person.get());  // 如果值非空就返回被包装的非空值 } else {   System.out.println(Optional.empty());  // 如果值为null,则返回EMPTY }

  isPresent还有一个重载方法isPresent(Consumer<? super T> consumer),其中Consumer是一个函数式接口,可以通过函数式接口给方法传递行为,这是Java 8 最牛的两个特性之一(另一个是Stream)。

  该方法的作用是Optional中的值非空,就执行isPresent方法,否则就不执行。如果执行了isPresent方法,至于在isPresent方法中要干什么事,则是借助Consumer函数式接口来传递。

String[] arr = {null, "非空"};
int index = new Random().nextInt(2);
Optional.ofNullable(arr[index])
  .ifPresent(x
-> System.out.println("原来真的" + x));  // 如果arr[index]非空,就输出"原来真的非空"

 

三、读取Optional中的值

  Optional类提供了一个get方法用来读取被包装的值,当值非空时,获取该值,否则抛出NoSuchElementException异常。这个方法必须要结合isPresent()一起用,先判断再取值。但是如果是这样,那我们压根就不需要使用Optional对值进行包装,直接用if对值进行判空,更简洁明了。所以这个方法用的不多。

String[] arr = {null, "非空"};
int index = new Random().nextInt(2);
Optional
<String> optional = Optional.ofNullable(arr[index]); if (optional.isPresent()) {   String s = optional.get(); }

 

四、操作Optional中的值

  Optional中提供了map和flatMap方法对Optional中的value进行操作,然后返回包装新值的Optional对象。

 

  • map : 如果Optional中的value非空,对value操作之后返回新值的Optional对象。如果value为空,则返回Optional的空对象。
String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> optional = Optional.ofNullable(arr[index]);
Optional<String> s = optional.map(e -> "原来真的" + e);

 

  • flatMap : 如果Optional中的value非空,对value操作之后返回新值的Optional对象,但是新值的Optional对象需要开发者手动封装。如果value为空,则返回Optional的空对象。
String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> optional = Optional.ofNullable(arr[index]);
Optional<String> s = optional.flatMap(e -> Optional.of("原来真的" + e));

  为什么会有这种区别,查看源码可以看到,map函数的返回值是Optional.ofNullable(mapper.apply(value)),已经被Optional封装了。而flatMap函数的返回值是Objects.requireNonNull(mapper.apply(value)),它没有被Optional封装,然而flatMap要求返回值是Optional<U>,所以只能手动封装。

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));
    }
}


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));
    }
}

  flatMap这种设计在多个Optional的链式调用时会用到。

 

五、Optional中的值为null怎么办

  Optional提供了三个方法:orElse、orElseGet 和 orElseThrow,这三个方法都是如果有非空值,就获取值,否则做对应的处理,具体如下:

  • orElse:当Optional中的值为空时,给Optional设置一个默认值。
String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> optional = Optional.ofNullable(arr[index]);
String s = optional.orElse("");  // 要么是 "非空",要么是 ""

  注意到如果optional非空时,并没有通过get取获取其中的值,orElse方法也可以获取。可以查看如下源码可知,如果value非空,返回value,否则返回other。

public T orElse(T other) {
    return value != null ? value : other;
}

 

  • orElseGet:当Optional中的值为空时,执行Supplier行为,通过这个行为给value设置默认值。这个方法存在是因为,有时候默认值无法简单给出,而是需要通过一系列的逻辑之后才能获得,这时候orElse方法就无法满足,只能使用这个方法。
String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> optional = Optional.ofNullable(arr[index]);
String s = optional.orElseGet(() -> {
  System.out.println("一系列逻辑");
  return "一系列逻辑之后获取的新值";
});

 

  • orElseThrow:当Optional中的值为空时,抛出一个异常。
String[] arr = {null, "非空"};
int index = new Random().nextInt(2);

Optional<String> optional = Optional.ofNullable(arr[index]);
String s = optional.orElseThrow(() -> new RuntimeException("没有值啊"));

 

六、过滤Optional对象

  通常我们说过滤都是针对一个列表,然而这里却是针对一个Optional对象,满足条件原样返回,不满足条件返回EMPTY

  Optional中提供一个 filter 方法来决定是否返回这个Optional对象,该方法接收函数式接口断言Predicate为入参,如果断言为真且value非空,则返回value非空的Optional对象,否则返回EMPTY;

Integer[] arr = {null, 10,  100};
int index = new Random().nextInt(3);

Optional<Integer> optional = Optional.ofNullable(arr[index]);
Optional<Integer> integer = optional.filter(t -> t > 50);  // 当arr[index]为null和10时,都是返回EMPTY

 

以上是Optional类的全部功能。

 

posted @ 2020-11-21 00:24  非洲铜  阅读(1001)  评论(0编辑  收藏  举报