Java 8 Optional使用介绍

概述

Java 8 引入了一个非常强大的特性就是 Optional 类,其主要解决的问题就是我们编程时常常遇到的空指针异常(NPE-NullPointerException)。在传统的编程方法里,通常都是以 if-else 条件语句对使用的对象进行判空,比如:

public String getVal(ClassA obj) {
    if (obj == null) {
        return "unknown";
    } else {
        return obj.getValue();
    }
}

但一旦对象的包含层次特别多时,条件语句的嵌套就会很复杂。而 Optional 类提供了一系列的方法可以通过链式编程的方式来简化代码,提供更便捷的形式。结合函数式编程来使用 Optional,达到流式调用,则更加能够感受到它的强大之处。

Optional 本身其实是一个装有对象的容器,该对象可以存在,也可以为 null。对象的有无影响着其方法的调用模式。

常用函数

Optional 主要提供了三种构造函数:

  • Optional.empty():返回一个空的 Optional 对象。
  • Optional.of(T value):内部调用一个私有的 new Optional<>(value) 构造方法,对象保存 value 的值,要求传入的 value 不能为 null,否则就会报空指针异常。
  • Optional.ofNullable(T value):根据传入 value 值的不同来构造不同的实体,value 为 null 时,则返回 Optional.empty() 的结果;否则返回 Optional.of(T value) 的结果。

除了这些构造方法外,Optional 还有一些常用的成员方法,如:

  • boolean isPresent():值为 null 则返回 false;否则返回 true。
  • Optional<T> filter(Predicate<? super T> predicate):入参函数 predicate 进行条件判断返回 boolean 值,如果对象有值且满足条件则返回包含该值的 Optional,否则返回空 Optional 对象。
  • Optional<U> map(Function<? super T, ? extends U> mapper):如果有值,则对其执行 mapper 函数得到返回值,该返回值作为 Optional 容器的对象进行返回,否则返回空 Optional 对象。
  • Optional<U> flatMap(Function<? super T, Optional<? extends U>> mapper):如果有值,则执行 mapper 返回 Optional 类型的返回值,否则返回空 Optional 对象。
  • void ifPresent(Consumer<? super T> consumer):如果有值,则对其执行 consumer 方法,否则不作处理。
  • T get():如果 Optional 对象有值则将其返回,否则抛出 NoSuchElementException。
  • T orElse(T other):如果有值则将其返回,否则返回指定的其他值。
  • T orElseGet(Supplier<? extends T> other):与上述 orElse() 方法类似。区别在于 orElse() 传入与返回值相同类型的默认值,而 orElseGet() 传入一个 Supplier 方法,用于生成一个默认值。
  • T orElseThrow(Supplier<? extends X> exceptionSupplier):如果有值则将其返回,否则抛出 supplier 方法创建的异常。

使用注意事项

我们在概述中提到过,Optional 的出现简化了我们对结果对象判空处理的代码实现。换个角度来说,Optional 作为一种结果返回类型,提供了对结果处理更为友好的解决方案。与传统冗余的条件语句相比,Optional 的优势在于,它能够通过链式调用对代码进行缩减,同时保持良好的代码可读性。从这个角度考虑,使用 Optional 需要判断当前场景是否能够被优化,避免滥用 Optional 的链式调用,因此上面说明的 Optional 常用函数在使用时需要注意到一些误区。

举个例子,本文开头的代码如果滥用 Optional 进行改造的话,可以得到下面的代码:

public String getVal(ClassA obj) {
    Optional<Object> op = Optional.ofNullable(obj);
    if (!op.isPresent()) {
        return "unknown";
    } else {
        return op.get().getValue();
    }
}

这样的改写操作其实跟源代码一样,只是用 isPresent() 方法替代了原来的 null 判断,而且代码反而变得不够整洁,显得冗余。一种正确的改写方式如下:

public String getVal(ClassA obj) {
    return Optional.ofNullable(obj)
        .map(b -> b.getValue())
        .orElse("unknown");
}

这样子的链式调用才是正确使用 Optional 的方式,即减少了 if-else 语句的冗余代码,又保证了良好的代码可读性。上面的语句还可以用 getter 方法进一步优化:

public String getVal(ClassA obj) {
    return Optional.ofNullable(obj)
        .map(ClassA::getValue)
        .orElse("unknown");
}

还有其他的一些 Optional 函数,在使用时注意不能只是单纯拿来替代传统代码的实现,也就是使用 Optional 经常需要避免一些误区:

  • 避免使用 isPresent() 来检查对象是否为 null,这跟 obj != null 没有区别。
  • 避免使用 get() 来获取对象,因为使用前需要使用 isPresent() 来判断对象是否为空,否则会抛出异常。所以应该使用 orElse()orElseGet() 或者 orElseThrow() 来获取结果。
  • 不要将 null 赋值给 Optional,每当对返回结果不确定时,就可以使用 Optional 作为返回类型。

除此之外还需要注意的是,Optional 类是没有实现 Serializable 接口的,也就是说它不支持序列化,因此我们不能将 Optional 类的对象作为其他类的字段。如果需要序列化的对象包含 Optional 对象,那么在 Java 8 的 Jackson 库支持把 Optional 当成普通对象,即空对象被当作 null,有值的对象也会看作其值本身,丢失了 Optional 的属性。所以就像上面所说,Optional 主要还是作为一种工具,作为一种返回类型对返回结果的数据以一种更友好的方式进行处理。

Optional 经典实践

上面对 Optional 类中的函数都作了简要说明,但是在实践中不是每一个方法都会经常被使用到。接下来提供一些经典场景的演示代码,如果在实践中还有其他常见的情况,欢迎大家进行补充。

  1. 获取指定字段,返回非null结果
// 返回个人简介内容,没有则返回空字符串
String intro = Optional.ofNullable(person.getIntro()).orElse("");

// 返回学生列表,没有则返回空列表
// 1-融合版
if (data != null) {
    List<Student> students1 = Optional.ofNullable(data.getStudents()).orElse(Collections.emptyList());
}
// 2-嵌套版
List<Student> students2 = Optional.ofNullable(Optional.ofNullable(data).orElse(new Data()).getStudents()).orElse(Collections.emptyList());
// 3-优雅版
List<Student> students3 = Optional.ofNullable(data).map(Data::getStudents).orElse(Collections.emptyList());
    
  1. 获取指定字段,不存在时通过工具方法生成赋值或抛出异常
// 补充缺失的uid字段
String uid = Optional.ofNullable(data.getUid()).orElseGet(() -> UUID.randomUUID().toString());
// uuid不能存在则抛出异常
String uuid = Optional.ofNullable(data.getUid()).orElseThrow(Exception::new);
  1. 进行条件筛选
// 列表大小小于5时返回空列表
List<Student> list = Optional.ofNullable(data).map(Data::getStudents).filter(s -> s.size() > 5).orElse(Collections.emptyList());

至于其他类似 ofemptygetisPresent,这些方法,其功能比较单一,或者有太多需要注意的前提,只有在某些特殊情况才会用到,这里就不在作详细解释了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值