Java - 使用 Java8 Optional 的正确姿势

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

转载自: https://yanbin.blog/proper-ways-of-using-java8-optional/


我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了:

Optional<User> user = ...... 
if (user.isPresent()) {
    return user.getOrders();
} else {
    return Collections.emptyList();
}

那么不得不说我们的思维仍然是在原地踏步, 只是本能的认为它不过是 User 实例的包装, 这与我们之前写成

User user = .....
if (user != null) {
    return user.getOrders();
} else {
    return Collections.emptyList();
}

实质上是没有任何分别. 这就是我们将要讲到的使用好 Java 8 Optional 类型的正确姿势.

在里约奥运之时, 新闻一再提起五星红旗有问题, 可是我怎么看都看不出来有什么问题, 后来才道是小星星膜拜中央的姿势不对. 因此我们千万也别对自己习以为常的事情觉得理所当然, 丝毫不会觉得有何不妥, 换句话说也就是当我们切换到 Java 8 的 Optional 时, 不能继承性的对待过往 null 时的那种思维, 应该掌握好新的, 正确的使用 Java 8 Optional 的正确姿势.

直白的讲, 当我们还在以如下几种方式使用 Optional 时, 就得开始检视自己了:

  1. 调用 isPresent() 方法时
  2. 调用 get() 方法时
  3. Optional 类型作为类/实例属性时
  4. Optional 类型作为方法参数时

isPresent()obj != null 无任何分别, 我们的生活依然在步步惊心. 而没有 isPresent() 作铺垫的 get() 调用在 IntelliJ IDEA 中会收到告警:

Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception.
 (调用 Optional.get() 前不事先用 isPresent() 检查值是否可用. 假如 Optional 不包含一个值, get() 将会抛出一个异常)

把 Optional 类型用作属性或是方法参数在 IntelliJ IDEA 中更是强力不推荐的

Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result". Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not. 
(使用任何像 Optional 的类型作为字段或方法参数都是不可取的. Optional 只设计为类库方法的, 可明确表示可能无值情况下的返回类型. Optional 类型不可被序列化, 用作字段类型会出问题的)

所以 Optional 中我们真正可依赖的应该是除了 isPresent()get() 的其他方法:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)

public T orElse(T other)

public T orElseGet(Supplier<? extends T> other)

public void ifPresent(Consumer<? super T> consumer)

public Optional<T> filter(Predicate<? super T> predicate)

public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X

我略有自信的按照它们大概使用频度对上面的方法排了一下序.

先又不得不提一下 Optional 的三种构造方式: Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty()

  1. Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.
  2. Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).

那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?

我本人的观点是: :

  1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(...))), 或者是一个非 null 常量时;
  2. 当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常, 立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.

现在才开始怎么去使用一个已有的 Optional 实例, 假定我们有一个实例 Optional<User> user, 下面是几个普遍的, 应避免if(user.isPresent()) { ... } else { ... } 这种方法的几种应用方式.

存在即返回, 无则提供默认值

return user.orElse(null);  //而不是 return user.isPresent() ? user.get() : null;
return user.orElse(UNKNOWN_USER); 

存在即返回, 无则由函数来产生

return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();

存在才对它做点什么

user.ifPresent(System.out::println);
 
//而不要下边那样
if (user.isPresent()) {
  System.out.println(user.get());
}

map 函数隆重登场

user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行:

return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
 
//上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
  return user.get().getOrders();
} else {
  return Collections.emptyList();
}

map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式:

return user.map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);

这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}

针对这方面 Groovy 提供了一种安全的属性/方法访问操作符 ?

user?.getUsername()?.toUpperCase();

Swift 也有类似的语法, 只作用在 Optional 的类型上.

用了 isPresent() 处理 NullPointerException 不叫优雅, 有了 orElse, orElseGet 等, 特别是 map 方法才叫优雅.

其他几个, filter() 把不符合条件的值变为 empty(), flatMap() 总是与 map() 方法成对的, orElseThrow() 在有值时直接返回, 无值时抛出想要的异常.

**小结:**使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.

最后, 最好的理解 Java 8 Optional 的方法莫过于看它的源代码 java.util.Optional, 阅读了源代码才能真真正正的让你解释起来最有底气, Optional 的方法中基本都是内部调用 isPresent() 判断, 真时处理值, 假时什么也不做.


参考链接:

  1. Java 8 Optional: How to Use it
  2. Tired of Null Pointer Exceptions? Consider Using Java SE 8’s Optional!
  3. 方法全解: http://blog.csdn.net/L_Sail/article/details/78868673
基于Apache SeaTunnel构建CDC数据同步管道 Apache SeaTunnel是一个数据集成开发平台,其发展经历了几个重要阶段:ETL时代(90年代):面向结构化数据库的数据同步,用于构建数据仓库。MPP和分布式技术流行:使用技术如Hive进行数据仓库的构建。此阶段主要使用mapreduce程序进行数据搬运和转换。数据湖技术流行:重视数据集成,强调先同步数据至数据湖仓储,再进行业务面向的转换和设计。CDC,即变更数据捕获,是一种捕获数据库变更事件(如插入、更新、删除)的技术。 阅读详情

相关推荐

蓝桥杯算法:打印图形——六芒星

【输入格式】 输入一行包含一个整数 n。 【输出格式】 输出一个六芒星,表示满足题意的图形。 【样例输入】 3 4 【样例输出】 【评测用例规模与约定】 对于所有评测用例,1 ≤ n ≤ 20。 总结: 第一次做图形题,做了蛮久,图形题有很多的方式去完成,它考验的是你能不能找到更简单的那一种方式. 我初看到这个图形,第一种想法是把这个六芒星当做六个小三角形来做,然后以某种规则排列一下,就能做出来,但是尝试了一下感觉太麻烦了。 然后我的第二种想法是,这个图形是对称图形,怎么能写出一半吗? 这样另一半想办

KeepCodeing 3813

Java中的Optional

常用的Optional对象的方法

unravelmc的博客 1万+

Cost-effective Attention Reuse across Multi-turn Conversations in Large Language Model Serving

通过多回合对话与人类互动是大型语言模型(LLM)的一个基本特征。然而,由于需要重复计算历史token的密钥值(KV)缓存,用于执行多回合会话的现有LLM服务引擎效率低下,从而导致高服务成本。为了解决这个问题,本文提出了AttentionStore,这是一种新的注意力机制,可以在多回合对话中重用KV缓存(即注意力重用),显著降低重复计算开销。AttentionStore维护一个分层的KV缓存系统,该系统利用经济高效的内存/存储介质为所有请求保存KV缓存。

c_cpp_csharp的专栏 377

干货,一文彻底搞懂 JavaOptional

史上最好的 Optional 指南,没有之一

沉默王二 3万+

详细分析Java中的Optional类以及应用场景

用开发的角度来讲,该类是 Java 8 中引入的一个用于处理可能为 null 的值的容器类。 它的设计目的是为了解决在代码中频繁出现的空指针异常问题。

码农研究僧的博客 1万+

Java8新特性之——Optional

Optional类是Java 8中引入的一个用于处理null值的容器类。它封装了某个类型的值,这个值可以是null,也可以是非null,通过它可以更优雅和简洁地处理包含null值的对象。Optional类的官方描述:翻译过来就是:一个可能包含null值的容器对象。如果值存在,isPresent()方法将会返回true,get()方法将会返回该值。

dongdong199033的博客 2748

Java 基础篇:深入理解Optional

OptionalJava 8 引入的一个容器类,用于表示一个可能为空(null)的值。通过使用Optional,我们可以避免常见的空指针异常(),并且使得方法签名更加清晰,增强代码的可读性。:通过显式的Optional表示值可能为空,减少空指针异常的发生。:显式地表明方法的返回值可能为空,而不是默默返回null。Optional提供了函数式接口,如map()flatMap()filter()等,能够方便地进行链式调用。用于创建一个非空的Optional。如果传入的值是null,将抛出。

3438

使用 Java 8 Optional正确姿势

我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 OptionalUser> user = ...... if (user.isPresent()) { return user.getOrders(); } else {

wisgood的专栏 3万+

使用 Java8 Optional正确姿势

我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 Optional<User> user = ...... if (user.isPresent()) { return user.getOrders(); } ...

qq_36733401的博客 408

Java8 Optional正确使用姿势

我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 Optional<User> user = ...... if (user.isPresent()) { return user.getOrders(); } ...

shadow_zed的博客 3496

(转)使用 Java8 Optional正确姿势

原文: https://unmi.cc/proper-ways-of-using-java8-optional/ 我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 Optional<User> user...

dianxianfei6692的博客 163

Java8Optional正确使用姿势

单纯的使用Optional并不能很好的解决业务中的NPE问题 - 简书 https://www.jianshu.com/p/f21afe47a3e7 单纯的使用Optional并不能很好的解决业务中的NPE问题 工作中经常会遇到NPE(NullPointException)问题怎么处理,得到最多的回答就是Optional来处理,但是单纯的使用Optional并不能很好的...

我是guyue,guyue就是我O(∩_∩)O 1339

使用 Java8 Optional正确姿势(转)

我们知道 Java 8 增加了一些很有用的 API, 其中一个就是 Optional. 如果对它不稍假探索, 只是轻描淡写的认为它可以优雅的解决 NullPointException 的问题, 于是代码就开始这么写了 Optional<User> user = ……if (user.isPresent()) {return user.getOrders();} else...

dichengyan0013的博客 98

Java8新特性(番外) 使用Optional正确姿势

我们知道Java8增加了一些很有用的API,其中一个就是Optional。如果对它不稍假探索,只是轻描淡写的认为它可以优雅的解决 NullPointException的问题,于是代码就开始这么写了: Optional<User> user = ...... if (user.isPresent()) { return user.getOrders(); } else { ...

戏流年的博客 1922

java8 Optional正确使用姿势

Java 8 如何正确使用 Optional import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import org.apache.commons.lang3.StringUtils; import java.util.Optional; @Data @Eq...

aipiannian6725的博客 108
上一篇: Java - 深入理解 Java 枚举类型
下一篇: Java - Java知识体系脑图
寒沧
博客等级 码龄13年 274粉丝 154原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值