Optional类

embedded/2024/11/25 14:49:11/

0.由来

实际 Java 开发过程中,尝试访问空引用的属性或者调用空引用的方法,会报 空指针异常NullPointerException)。处理可能为 null 的值时,需要增加很多 条件判定,比如:

💗User:

java">@Data
@AllArgsConstructor
public class User {String name;String fullName;
}

💙UserRepository:

java">public class UserRepository {public User findUserByName(String name){if(name.equals("Shwen")){return new User("Shwen","Shwen shen");} else {return null;}}
}

👉OptionalTest:

java">public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();User user = userRepository.findUserByName("Shwen");if(user!=null){System.out.println(user.getFullName());} else {User defaultUser = new User("Stark", "Tony Stark");System.out.println(defaultUser.getFullName());}}
}

代码会有很多的条件判断,难以阅读与维护。

1.概述

Optional 类引入了一种显式的方式来处理可能为空的对象,强制程序员在可能为空的情况下进行显式的处理,以避免空指针异常。

Optional 是一个容器对象,用于封装可能为 null 的值。当一个方法声明返回 Optional<T> 时,它将明确地表示该方法可能会返回 null。这种设计模式有助于减少空指针异常的风险,并鼓励更好的编程实践。

Optional 类提供了一系列方法来方便地操作内部的值。常用的方法有getorElseorElseGetorElseThrow等。

Optional 的设计也考虑了 函数式编程 的原则,可以与 Lambda 表达式和 StreamAPI 等特性结合使用,可以进行链式调用替代命令式编程的方式(通过编写if条件语句检查null值)。

2.Optional对象的方法

2.1 创建Optional实例

  • of:创建不为 null 的对象,若 value 为 null,则抛出 NullPointerException 异常。
java">Optional.of(new User("Shwen","Shwen shen"));
  • ofNullable:创建可能为 null 的对象
java">Optional.ofNullable(new User("Shwen","Shwen shen"));
  • empty:创建的对象为 null
java">Optional.empty();

💙改造UserRepository:

java">public class UserRepository {public Optional<User> findUserByName(String name){if(name.equals("Shwen")){return Optional.ofNullable(new User("Shwen","Shwen shen"));} else {return Optional.empty();}}
}

2.2 判断方法

  • isPresent:用于检查 optional 内是否存在值,返回为 boolean,存在为 true,不存在为 false
java">boolean flag = optionalUser.isPresent();

2.3 获取方法

  • get:若取值对象为 null,会报 NoSuchElementException 异常(并非 NullPointerException 异常)。
java">User user = optionalUser.get();
  • orElse:用于获取值或在值为 null 的情况下提供一个默认值。
java">public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();Optional<User> optionalUser = userRepository.findUserByName("Shwen2");// orElse 用于获取值,或在值为 null 的情况下提供一个默认值。User user = optionalUser.orElse(new User("Stark","Tony Stark"));System.out.println(user.getFullName());}
}

optionalUser 有值的情况下,orElse 返回值;值为 null 的情况下,返回默认值 new User("Stark","Tony Stark")

  • orElseGet:功能同 orElse,区别在于方法的参数为 Supplier 的函数式接口,需要使用 Lambda 表达式实现。
java">User user = optionalUser.orElseGet(() -> new User("Stark","Tony Stark"));

📌orElseorElseGet 区别如下:

  1. orElse 入参是对象,orElseGet入参是 Supplier 的函数式接口
  2. orElse 即使获取的数据不为 null,仍会创建一个新 User 对象。而 orElseGet 不会创建。因此,最好使用只有读取的数据为 null 的时候才会新建对象的 orElseGet 方法。
  • orElseThrow:用于在 Optional 对象中的值为 null 时抛出一个指定的异常。
java">User user = optionalUser.orElseThrow(() -> new RuntimeException("User not found"));

2.4 判断获取方法

  • ifPresent:参数中若对象不为 null,则会执行 Labmda 中的方法;若参数对象为 null,则不会执行Labmda中的方法,也不会报错
java">optionalUser.ifPresent(user -> System.out.println(user.getFullName()));
  • ifPresentOrElse:当我们希望值为 null 时进行其他操作,需要使用 ifPresentOrElse 方法。⚠️注:ifPresentOrElse 方法是 Java 9 引入的。
java">optionalUser.ifPresentOrElse(user -> System.out.println(user.getFullName()),()->System.out.println("User not found"));
  • filter:若满足 filter 方法中的条件,则会返回包含值的 Optional 对象,如果不满足,则返回空的 Optional 对象。
java">Optional<User> optionalUser2 = optionalUser.filter(user -> user.getFullName().equals("Shwen shen"));
System.out.println(optionalUser2.isPresent());

2.5 map与flatMap方法

2.5.1 map

java">public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
java">public class OptionalTest {public static void main(String[] args) {UserRepository userRepository = new UserRepository();Optional<User> optionalUser = userRepository.findUserByName("Shwen2");Optional<String> optional = optionalUser.map(User::getFullName);System.out.println(optional.orElse("User not found"));}
}输出:
User not found

Optional 中的值进行转换,若值为 null,则 map 方法什么也不会做,直接返回空的 Optional 对象。map 方法不会改变原始的 Optional 对象,而返回新的 Optional 对象,因此可以链式调用进行多个转换操作。

2.5.2 flatMap

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

flatMap 方法用于扁平化嵌套的 Optional 结构,以避免引入不必要的嵌套层级,具体为 flatMap 的转换函数返回的必须是另一个 Optional 对象,意味着 flatMap 方法可以用于嵌套的 Optional 情况,可以将两个为嵌套关系的 Optional 对象转换为一个。

如果原始的 Optional 对象为 null,或转换函数返回的 Optional 对象为 null,那么最终得到的也是为 nullOptional 对象。

若只需要对 Optional 对象中的值进行转换,而不需要嵌套的 Optional,那么使用 map 方法更合适。如果要进行一些操作返回另外一个 Optional 对象,flatMap 方法更合适。

2.6 Stream方法

Optionalstream 方法,可以将Optional 对象转换为 Stream 对象,对其中的值进行流操作

如果 Optional 对象包含值,则将这个值封装到一个 Stream 流中,如果 Optional 对象为空,则创造一个为空的 Stream 流。

java">Stream<String> stream = optionalUser.map(User::getName).stream();
stream.forEach(System.out::println);

⚠️注:stream 方法是 Java 9 引入的。

3.不适合场景

  • 不应该用于类的字段,会增加内存消耗,并使序列化变得复杂;
  • 不应该用于方法参数,使方法的理解和使用变得复杂;
  • 不应用于构造器参数,迫使调用者创建 Optional 实例,应该通过构造器重载解决;
  • 不应该用于集合的参数,集合已经很好的处理空集合的情况,没必要使用 Optional 包装集合;
  • 不建议使用 get 方法,若为 null 会报错。建议使用 orElseGet

4.适用场景

应用场景一般用于方法返回值或者集合操作中。

场景一:
Optional 主要用作返回类型。在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。

java">User user = new User("shwen", null);
String fullName = Optional.ofNullable(user.getFullName()).orElse("未知名称");

场景二:
在对集合进行操作时,某些操作可能返回 null(例如在查找元素时)。使用 Optional 可以更好地处理这些情况,而不是直接返回 null

java">public void whenEmptyStream_thenReturnDefaultOptional() {List<User> users = new ArrayList<>();User user = users.stream().findFirst().orElse(new User("default", "1234"));assertEquals(user.getEmail(), "default");
}

场景三:
设计 API 时,如果某个方法的返回值可能为空,使用 Optional 可以使 API 更加健壮和易于理解。

java">public Optional<Product> findProductById(String productId) {// 根据产品ID查找产品,产品可能不存在// 使用 Optional 封装返回值// 返回 Optional.ofNullable(product) 或 Optional.empty()
}

场景四:
当处理多层嵌套的对象时,使用 Optional 可以避免深层次的 null 检查,使代码更加简洁。

java">public class UserInfo {private String name;private Integer age;private Address address;// 省略构造函数和getter/setter
}public class Address {private String city;// 省略构造函数和getter/setter
}public class OptionalTest {public static void main(String[] args) {UserInfo userInfo = new UserInfo();userInfo.setName("张三");userInfo.setAge(25);userInfo.setAddress(null);String cityName = Optional.ofNullable(userInfo.getAddress()).map(Address::getCity).orElse("未知城市");System.out.println(cityName);}
}输出:
未知城市

本例先通过 Optional.ofNullable 获取 Address 信息,返回类型是 Optional<Address>。再通过 map 方法获取城市名称。由于 address 字段为 null,因此 getCity() 方法返回的 Optional<String> 也是空的。最后,通过 orElse 方法返回默认值 “未知城市”。

5.小结

Optional 提供了一系列方法来判断值是否为空、获取值、通过 Lambda 表达式来处理值等操作,使得代码更加健壮和可读性更好。通过使用 Optional,开发者可以更加清晰地表达值的可能为空的情况,并采取相应的处理措施,有效避免了空指针异常的发生。

⚠️注: Java 9Optional 类添加了三个方法:or()ifPresentOrElse()stream()

最后提一嘴,Optional 虽然可以提高代码的健壮性,但需要注意不要滥用。主要缺点如下:

  • 造成代码冗长,本来一个null值就可以解决
  • 引入多一个对象多一个开销,如果本身追求速度,就不用刻意这样
  • 如果嵌套多层对象还这么使用,增加代码维护难度
  • 对新手不友好

📖Optional实战案例


http://www.ppmy.cn/embedded/140408.html

相关文章

C语言中的结构体,指针,联合体的使用

目录 1. 概述2. 定义和初始化3. 成员的使用4. 结构体数组5. 结构体套结构体6. 结构体赋值7. 结构体和指针8. 结构体作为函数参数9. 共用体&#xff08;联合体&#xff09;10. typedef就是取别名总结 1. 概述 数组&#xff1a;连续的相同数据类型的集合 结构体&#xff1a;不同…

Java将PDF保存为图片

将 PDF 文件转换为图片是常见的需求之一&#xff0c;特别是在需要将 PDF 内容以图像形式展示或处理时。其中最常用的是 Apache PDFBox。 使用 Apache PDFBox Apache PDFBox 是一个开源的 Java 库&#xff0c;可以用来处理 PDF 文档。它提供了将 PDF 页面转换为图像的功能。 …

SSM--SpringMVC复习(一)

SpringMVC Spring MVC 是一个实现了MVC设计模式的请求驱动类型的轻量级Web框架&#xff0c; 本质上相当于 Servlet&#xff0c;以 DispatcherServlet 为核心&#xff0c;负责协调和组织不同组件以完成请求处理并返回响应。通过控制器&#xff08;C&#xff09;对模型数据&…

十五届蓝桥杯赛题-c/c++ 大学b组

握手问题 很简单&#xff0c;相互牵手即可&#xff0c;但是要注意&#xff0c;第一个人只能与其他49个人牵手&#xff0c;所以开头是加上49 #include <iostream> using namespace std; int main() {int cnt0;for(int i49;i>7;i--){cnti;//cout<<i<<&quo…

第十章 JavaScript的运用

10.1 JavaScript概述 1.JavaScript简介 JavaScript是一种基于对象&#xff08;Object&#xff09;和事件驱动&#xff08;Event Driven&#xff09;并具有安全性能的脚本语言&#xff0c;能够与HTML&#xff08;超文本标记语言&#xff09;、Java语言一起在Web页面中与Web客…

银河麒麟v10 x86架构二进制方式kubeadm+docker+cri-docker搭建k8s集群(证书有效期100年) —— 筑梦之路

环境说明 master&#xff1a;192.168.100.100 node: 192.168.100.101 kubeadm 1.31.2 &#xff08;自编译二进制文件&#xff0c;证书有效期100年&#xff09; 银河麒麟v10 sp2 x86架构 内核版本&#xff1a;5.4.x 编译安装 cgroup v2启用 docker版本&#xff1a;27.x …

Mac配置maven环境及在IDEA中配置Maven

Mac配置maven环境及在IDEA中配置Maven 1. 介绍 Maven是一款广泛用于Java等JVM语言项目的工具&#xff0c;它以项目对象模型&#xff08;POM&#xff09;为基础进行项目管理&#xff0c;通过POM文件来定义项目信息和依赖关系。同时&#xff0c;它也是构建自动化工具&#xff0…

Sparrow系列拓展篇:消息队列和互斥锁等IPC机制的设计

前言 笔者这几天利用空闲时间为Sparrow添加了消息队列和互斥锁&#xff0c;已经有十余天没有更过文章了&#xff0c;今天下笔想写一写博客&#xff0c;遂继续为Sparrow更新一篇拓展篇。 其实也没什么好讲的&#xff0c;信号量、消息队列、互斥锁这些IPC机制都大差不差&#x…