Bootstrap

可选值 Optional 的妙用,让代码更短更简洁

一、简介

Optional 是什么呢?Optional 类是 jdk8 引入的一个新的特性,它可以帮助我们编写更简短的代码从而实现和使用了多层 if 语句嵌套相同的功能。
Optional 类是一个可以为 null 的容器对象,如果调用 ifPresent() 方法为 true,则可以通过 get() 返回获取到对象值。
Optional 新特性的引入很好的解决了空指针异常的问题。

二、Optional 常用操作

2.1 三个构建函数

2.1.1 Optional.of(obj) 【obj 不能为 null 值】

要求传入的 obj 不能是 null 值的, 否则直接报NullPointerException 异常,返回一个指定非null值的Optional。

2.1.2 Optional.ofNullable(obj)【obj 可以为 null 值】

ofNullable() 构建方法则允许传入的 对象 是一个 null 值,如果为非空,返回 Optional 描述的指定值,否则返回空的 Optional。

2.1.3 Optional.empty()【构建空的Optional 对象】

返回一个空的 Optional 对象,一般不多用。

2.2 提供的操作方法

方法描述
isPresent()如果值存在,则返回 true,否则返回 false
get()如果 Optional 中存在值,则返回,否则抛出 NoSuchElementException 异常
orElse(T other)如果 Optional 中存在值,则返回,否则返回 other 对象
orElseGet(Supplier<? extends T> supplier)如果 Optional 中的值存在,则返回,否则返回 supplier 函数处理的结果
orElseThrow()如果 Optional 中存在值,则返回,否则抛出 NoSuchElementException 异常
orElseThrow(Supplier<? extends X> exceptionSupplier)如果 Optional 中存在值,则返回,否则抛出给定的异常,eg: Optional.ofNullable(pid).orElseThrow(IllegalArgumentException::new);
filter(Predicate<? super T> predicate)如果满足给定 predicate 条件的值,则返回,否则返回 空的 Optional
map(Function<? super T, ? extends U> mapper)返回按照 mapper 函数处理的结果,如果处理中没有值返回,则返回空的 Optional
flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)返回按照 mapper 函数处理的结果并进行其他Optional操作,如果处理中没有值返回,则返回空的 Optional

三、Optional 使用好处(不限于)

3.1 避免空指针异常(NullPointerException)

  • 空指针异常是很常见的问题,很多情况下,有一个对象偶尔是 null,就容易出现空指针异常,使用Optional可以将这种风险降低。
  • 比如说有一个 User 对象,我们要获取 address 属性,而 address 里面还有省市区等字段,在传统方法中,我们会用 if(address != null) 来判断,基本会这样写:
 User user = getUser();
 String cityName = null;
 if (user!= null) {
     Address address = user.getAddress();
     if (address!= null) {
         cityName = address.getCity();
     }
 }
  • 这种嵌套的if语句很繁琐,而且容易遗漏检查导致空指针异常。而使用Optional可以这样改写:
 Optional<User> userOptional = Optional.ofNullable(getUser());
 String cityName = userOptional.flatMap(User::getAddress)
        .map(Address::getCity)
        .orElse(null);

Optional 的操作链会在遇到 null 值时自动跳过后续的操作,而不是抛出空指针异常。flatMap 和 map 操作会根据Optional中的值是否存在来进行相应的处理,只有当所有的 Optional值都存在时,才会最终返回一个非 null 的值。

3.2 增强代码的可读性

Optional使得代码的意图更加清晰。通过使用Optional的方法,如 orElse、orElseGet、orElseThrow 等,阅读代码的人可以很容易地理解当值不存在时应该采取什么样的行为。例如,下面的代码:

 Optional<String> optionalValue = getOptionalValue();
 String result = optionalValue.orElse("default value");

从这段代码中可以很清楚地看到,如果optionalValue不存在,就会使用"default value"作为结果。相比传统的if - else语句来处理null值,这种方式更加简洁明了,尤其是在复杂的业务逻辑中,能够提高代码的可维护性。

3.3 支持函数式编程风格

Optional 是 Java 8 引入的函数式接口,它提供了许多函数式编程方法,如map、flatMap和filter等。这些方法允许我们以一种链式调用的方式来处理可能为null的值,符合函数式编程的理念。
例如,我们可以使用filter方法来对Optional中的值进行条件筛选。假设我们有一个获取整数的Optional方法,并且我们只想获取大于 10 的整数:

   Optional<Integer> optionalInt = getOptionalInteger();
   Optional<Integer> filteredOptional = optionalInt.filter(i -> i > 10);

这种方式可以方便地在一个操作链中对值进行多个条件的筛选和转换,使得代码更加紧凑和高效,同时也更容易与其他函数式编程的组件(如Stream)结合使用。

四、Optional 使用不好处

4.1 简单场景下,增加代码复杂度

对于一些非常简单的、可以确定不会出现空指针异常的情况,使用Optional可能会让代码变得更加复杂。例如,只是简单地从一个已知非空的对象中获取属性,使用Optional就有些多余。
比如有一个简单的 Person 对象,映射到表中的 name 字段是 not null ,它则是一个非空的 name 属性,这个时候用 Optional 就显得没必要了。

4.2 性能开销(相对较小但存在)

  • 创建 Optional对象以及调用它的方法会带来一定的性能开销。虽然在大多数情况下,这种开销可以忽略不计,但在对性能要求极高的场景下,比如性能敏感的算法或者频繁调用的底层代码中,过多使用Optional可能会产生影响。
  • Optional是一个对象,它的创建和操作涉及到对象的分配、方法调用等操作。与简单的直接引用和操作基本类型或普通对象相比,会消耗更多的内存和时间。

4.3 不恰当使用导致理解困难

如果没有正确理解Optional的语义和用法,可能会导致代码更难理解。例如,过度嵌套Optional操作或者错误地使用map、flatMap等方法,会让代码逻辑变得混乱。假设多个 Optional 操作嵌套在一起,而且没有清晰的代码注释,看如下代码:

 Optional<Order> orderOptional= getOrderOptional();
 Optional<List<Product>> productsOptional = orderOptional.flatMap(Order::getProducts);
 Optional<Integer> totalPriceOptional = productsOptional.map(products -> products.stream()
       .map(Product::getPrice).reduce(Integer::sum));

这样的代码如果没有对Optional的深入理解和良好的命名规范,其他开发人员可能很难理解其真正的意图,尤其是在处理错误情况和null值传播方面。