处理Null的神器Optional

文摘   2024-10-29 08:00   新疆  

最新实战案例锦集:《Spring Boot3实战案例合集》持续更新,每天至少更新一篇文章,订阅后将赠送文章最后展示的所有MD文档(学习笔记)。

环境:SpringBoot3.2.5



1. 简介

在Java开发中,处理空值往往是一个令人困扰的问题。空值不仅可能导致NullPointerException,还会引发复杂的if条件判断和易错的逻辑。幸运的是,Java 8引入了Optional类,它提供了一种简洁而强大的处理空值的方法,同时提升了代码的质量。Optional作为一个容器对象,可以包含或不包含非空值,并提供了一系列实用的方法来操作其内容。本文将深入探讨 Optional 在Java中的多种应用场景,并展示如何利用这个强大的类来编写更加清晰、表达力强且健壯的代码。

现在,让我们通过几个具体的例子来详细了解 Optional 的使用方法。

2. 实战案例

2.1 处理NPE

Optional 最常见的用例之一是避免NullPointerException。通过将一个可能为null的值包裹在 Optional 中,你可以安全地访问该值而不必担心会出现 NullPointerException。例如,如果你有一个方法返回的值可能是null,你可以改为返回一个Optional,并使用Optional 的方法来安全地访问该值。

String value = null ;Optional<String> opt = Optional.ofNullable(value) ;if (opt.isPresent()) {  System.err.println(opt.get()) ;}

该示例中,我们从一个可能为空的值创建了一个 Optional。然后,我们使用 Optional.isPresent() 方法检查该值是否存在,并使用 Optional.get() 安全地访问该值。

2.2 处理异常

Optional 的另一个引人注目的用例是简化异常处理。你可以使用 Optional.orElseThrow() 方法在值不存在时抛出异常。这可以使代码更简洁、更易读,尤其是在处理多个潜在异常时,如下示例:

Optional<String> opt = Optional.empty() ;String value = opt.orElseThrow(() -> new RuntimeException("没有数据!")) ;

我们创建了一个空的 Optional,并使用 Optional.orElseThrow() 在值不存在时抛出 RuntimeException。这简化了异常处理代码,使其更加简洁易读。

2.3 延迟处理错误

Optional 也非常适合延迟决定需要发生的事情,以防在更高的上下文中出现错误。现在不是由函数决定行为,而是由调用者决定在调用者的上下文中什么是最好的。

假设我们有一种方法,可以根据图书的 ISBN 在图书馆中搜索图书:

private final BookRepository bookRepository ;public static Book queryBookByIsbn(String isbn) {  Book book = bookRepository.queryByIsbn(isbn) ;  if (book != null) {    return book ;  } else {    throw new IllegalArgumentException(String.format("未找到【%s】对应的图书", isbn));  }}

如果查询不到给定 ISBN 的图书,该方法会抛出异常。如何处理这个错误由方法本身决定。那么我们可以使用 Optional 将错误处理延迟给调用者:

public static Optional<Book> queryBookByIsbn(String isbn) {  Book book = bookRepository.queryByIsbn(isbn) ;  if (book != null) {      return Optional.of(book);  } else {      return Optional.empty();  }}// 调用Optional<Book> book = bookService.queryBookByIsbn("SN001") ;if (book.isPresent()) {  // TODO} else {  System.err.println("查无此书");}

如果查询不到给定 ISBN 的图书,该方法会返回一个空的Optional,表示发生了错误。否则,它将返回一个包含Book的Optional。

2.4 链式操作

我们还可以使用 Optional#flatMap() 方法对 Optional 进行链式处理。这对于访问嵌套在其他对象中的值非常有用。例如,如果一个对象包含另一个可能为空的对象,则可以使用 Optional.flatMap() 访问嵌套的值,而不必冒出现 NullPointerException 的风险,如下示例:

Optional<Author> opt = Optional.ofNullable(book)  .flatMap(Book::getAuthor) ;

这样既简化了代码,又避免了空值检查。

2.5 定义默认值

Optional 也可用于定义缺失值或空值的默认值。可以使用 Optional.orElse() 来指定在 Optional 为空时返回的默认值。这可以使你的代码更有弹性,并减少因空值或缺失值导致错误的可能性。

String value = null ;String result = Optional.ofNullable(value)  .orElse("default") ;

这使得代码更有弹性,并避免了因空值或缺失值导致的错误。

2.6 避免模板代码

通过使用 Optional,可以避免编写检查空值的模板代码。Optional 提供了一种简洁而富有表现力的方法来处理空值,从而使代码更具可读性和可维护性,如下示例:

String value = null ;Optional<String> opt = Optional.ofNullable(value) ;String result = opt.map(s -> s.toUpperCase())  .orElse("default") ;

这样就不需要 if 语句来检查 null,使代码更加简洁易读。

2.7 组合方法

Optional 可用于更简洁、更有表现力地将方法组合在一起。通过用 Optional 封装方法的返回值,可以使用 Optional 方法将多个方法的调用串联起来。这可以使代码更易读、更易懂,如下示例:

Optional<String> opt = Optional.of("Pack")  .map(s -> s.toUpperCase())  .filter(s -> s.startsWith("P")) ;

这样,我们就可以在一个表达式中将多个方法调用组合在一起。

2.8 集合处理

Optional 可用于以更简洁、可读性更强的方式处理 Optional 值集合。例如,如果你有一个可选值集合,可以使用 Optional.stream() 创建一个非空值流,然后使用流方法对这些值执行操作。这样可以使代码更具表现力,也更容易理解,如下示例:

List<Optional<String>> list = Arrays.asList(  Optional.empty(),  Optional.of("Pack"),  Optional.of("Zak"));String result = list.stream()  .flatMap(Optional::stream)  .collect(Collectors.joining(",")) ;

我们创建了一个 Optional 列表,使用 Optional.stream() 创建了一个非空值流,然后使用stream方法将非空值连接成一个字符串。这样,我们就可以更优雅、处理 Optional 值集合。

2.9 简化配置

Optional 可为配置参数提供默认值,从而简化配置管理,如下示例:

@Value("${app.title}")private String title ;String value = Optional.ofNullable(title)  .orElse("Default Title") ;

这使得配置更具弹性,并避免了因配置参数缺失而导致的错误。

2.10 默认实现

Optional 可用于为可能未实现的方法提供默认实现。这可以通过提供默认行为来简化代码,必要时可以重载默认行为,如下示例:

public interface UserService {  default Optional<User> getUser(){    return Optional.empty() ;  }}

在本例中,我们定义了一个接口,为返回可选值的方法提供默认实现。这就简化了接口的实现,并提供了一种默认行为,必要时可以重写。

2.11 增强可读性

通过提供一种简洁明了的方法来处理空值,Optional 可以用来提高代码的可读性。例如,可以使用 Optional 方法更优雅地处理空值,而不是使用 if 语句编写检查空值的代码。这样可以使代码更易读、更易懂,如下示例:

Optional.ofNullable(value)  .ifPresentOrElse(    v -todo(v),     () -todoElse()  ) ;

在本例中,我们从一个潜在的空值创建一个 Optional,并使用 Optional.ifPresentOrElse() 在值存在时执行一个操作,在值不存在时执行另一个操作。这提供了一种简洁明了的方法来处理空值,并使代码更具可读性和可维护性。

2.12 处理复杂对象

Optional 可用于处理包含可选值的复杂对象。这可以使你的代码更具弹性,更易于维护,如下示例:

Optional<Color> opt = Optional.ofNullable(square)  .map(Square::upperLeft)  .map(ColoredPoint::color) ;

通过map方法能非常方便的处理数据。

以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

进阶!@ConfigurationProperties注解高级用法你知道吗?

手写@RefreshScope,很简单嘛!

强烈建议JDK升级到17,因为这些功能

SpringBoot多租户3种架构实现方案详解

基于SpringBoot通过3种方式轻松搞定敏感字段加密处理

Spring Boot启动时必须对配置信息做有效性校验

SpringBoot项目中这10个开发技巧你都知道吗?

玩转Redis!非常强大的Redisson分布式集合,少写60%代码

SpringBoot3第三方接口调用10种方式,最后一种你肯定没用过

SpringBoot中Controller接口参数还可以这样玩?

OpenFeign新功能,终于支持了这2项功能

Java开发人员必须掌握的11种干净代码最佳实践

SpringBoot自带Controller接口监控,赶紧用起来

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章