Java8的Optional简介

文章目录

  • 环境
  • 背景
    • 方法1:直接获取
    • 方法2:防御式检查
    • 方法3:Java 8的Optional
      • 概述
      • map()
        • 测试
      • flatMap()
        • 测试
  • 总结
  • 参考

注:本文主要参考了《Java 8实战》这本书。

在这里插入图片描述

环境

  • Ubuntu 22.04
  • jdk-17.0.3.1 (兼容Java 8)

背景

现有 InsuranceCarPerson 类,定义如下:

  • Insurance
public class Insurance {private String name;public String getName() {return name;}......
}
  • Car
public class Car {private Insurance insurance;public Insurance getInsurance() {return insurance;}......
}
  • Person
public class Person {private Car car;public Car getCar() {return car;}......
}

现在需要获取某个Person的Car的Insurance名字。

方法1:直接获取

最简单粗暴的写法,就是:

        String name = person.getCar().getInsurance().getName();

注:像这样 a.b.c 的调用方式,貌似违反了迪米特法则。

然而,我们知道,在Java里,如果操作一个空对象,则会抛出异常。

最简单的例子:

        Person person = null;person.getCar();

此处 person 是空对象,所以调用 person.getCar() 方法时,会抛出 NullPointerException 异常:

Exception in thread "main" java.lang.NullPointerException: Cannot invoke "Person.getCar()" because "person" is nullat Test0917.main(Test0917.java:8)

所以,这种方法显然是不可取的。

方法2:防御式检查

为了避免NPE,我们必须采取“防御式编程”,也就是说,在操作对象时,先要确保它不是空对象。

        String name = null;if (person != null) {Car car = person.getCar();if (car != null) {Insurance insurance = car.getInsurance();if (insurance != null) {name = insurance.getName();}}}

防御式检查可以避免NPE,然而代价是代码变得臃肿而且难以理解和维护。原先只有1行代码,现在变成了10行。

如果使用三元运算符 ? :,固然可以简化为一行代码:

        String name = (person == null)? null: ((person.getCar() == null)? null: ((person.getCar().getInsurance() == null)? null: (person.getCar().getInsurance().getName())));

但是这行代码实在是太令人费解了,同时非常难以维护。

方法3:Java 8的Optional

概述

方法1很简单,但有漏洞,方法2弥补了漏洞,但带来了复杂度。那如何才能兼顾简单与安全呢?

Java 8引入了Optional类,这是一个封装“Optional值”的类。顾名思义,既然是optional,其封装的值可能存在,也可能不存在。

举个例子来说,一个 Optional<Car> 对象,可能封装了一个非空的 Car 对象,也可能封装了一个 null 对象。

现在简单看一下创建Optional的语法:

  • 创建一个空的Optional对象:
        Optional<Car> car1 = Optional.empty();
  • 创建一个非空的Optional对象:
        Optional<Car> car2 = Optional.of(car); // car不能为null

其中, car 是一个Car对象,而且必须是非空的,否则,这一步会直接抛出NPE。

  • 如果想创建一个可接受空值的Optional对象:
        Optional<Car> car3 = Optional.ofNullable(car); // car可以为null

从Optional对象获取其封装对象:

  • get()
        Optional<Car> car4;......Car car = car4.get(); // 获取封装的Car对象,若其为空,则抛出NoSuchElementException
  • orElse()
        Car car = car4.orElse(new Car()); // 获取封装的Car对象,若其为空,则返回指定值

map()

看到这里,你可能还是一头雾水,到底Optional能给我们带来什么好处?

Optional的神奇之处在于,它和流(Stream)的用法非常相似,可以做 map()filter() 等操作。事实上,它就相当于只包含0个或者1个对象的流。

在方法1中:

        String name = person.getCar().getInsurance().getName();

可见,代码逻辑是依次获取Car、Insurance、Name。

通过Optional,可以把它转换为如下操作:

        Optional<Person> optPerson = Optional.ofNullable(person);String name = optPerson.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName).orElse("Unknown");

本例中, map() 操作把Optional所持有的对象做了映射,比如本来是持有Person( Optional<Person> ),变成持有Car( Optional<Car> ),再变成持有String( Optional<String> ),最终获取String对象。

如果持有对象不为空,则对其做map操作,若持有对象为空,则不做处理。这就大大简化了代码,提高了可读性和可维护性。

测试

完整的测试如下:

  • 测试1:Person,Car,Insurance都不为空:
        Insurance insurance1 = new Insurance("ABC");Car car1 = new Car(insurance1);Person person1 = new Person(car1);Optional<Person> optPerson1 = Optional.ofNullable(person1);System.out.println(optPerson1.map(Person::getCar));System.out.println(optPerson1.map(Person::getCar).map(Car::getInsurance));System.out.println(optPerson1.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson1.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional[Car{insurance=Insurance{name='ABC'}}]
Optional[Insurance{name='ABC'}]
Optional[ABC]
ABC
  • 测试2:Person和Car不为空,Insurance为空:
        Car car2 = new Car(null);Person person2 = new Person(car2);Optional<Person> optPerson2 = Optional.ofNullable(person2);System.out.println(optPerson2.map(Person::getCar));System.out.println(optPerson2.map(Person::getCar).map(Car::getInsurance));System.out.println(optPerson2.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson2.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional[Car{insurance=null}]
Optional.empty
Optional.empty
Unknown
  • 测试3:Person不为空,Car和Insurance为空:
        Person person3 = new Person(null);Optional<Person> optPerson3 = Optional.ofNullable(person3);System.out.println(optPerson3.map(Person::getCar));System.out.println(optPerson3.map(Person::getCar).map(Car::getInsurance));System.out.println(optPerson3.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson3.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional.empty
Optional.empty
Optional.empty
Unknown
  • 测试4:Person,Car,Insurance都为空:
        Optional<Person> optPerson4 = Optional.ofNullable(null);System.out.println(optPerson4.map(Person::getCar));System.out.println(optPerson4.map(Person::getCar).map(Car::getInsurance));System.out.println(optPerson4.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson4.map(Person::getCar).map(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional.empty
Optional.empty
Optional.empty
Unknown

可见,在任何情况下,都能得到预期的结果,不会报错。

flatMap()

既然Person不一定拥有Car,Car也不一定拥有Insurance,所以应该都是optional的。

假设修改类如下:

  • Car
public class Car {private Insurance insurance;public Optional<Insurance> getInsurance() {return Optional.ofNullable(insurance);}......
}
  • Person
public class Person {private Car car;public Optional<Car> getCar() {return Optional.ofNullable(car);}......
}

注意:要确保 getXxx() 返回的Optional对象本身不要为空,否则,就又得加上判断逻辑了。

相应的, map() 操作需要转换为 flatMap()

        Optional<Person> optPerson = Optional.ofNullable(person);String name = optPerson.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown");

注意:这里之所以使用 flatMap() ,是因为 Person::getCar 返回的是 Optional<Car> ,需要用 flatMap() 将其扁平化(去掉中间层的Optional)。

注意:要确保 getXxx() 返回的Optional对象本身不要为空,否则,在调用 flatMap() 时会抛出NPE。

测试

下面是完整的测试:

  • 测试1:Person,Car,Insurance都不为空:
        Insurance insurance1 = new Insurance("ABC");Car car1 = new Car(insurance1);Person person1 = new Person(car1);Optional<Person> optPerson1 = Optional.ofNullable(person1);System.out.println(optPerson1.flatMap(Person::getCar));System.out.println(optPerson1.flatMap(Person::getCar).flatMap(Car::getInsurance));System.out.println(optPerson1.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson1.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional[Car{insurance=Insurance{name='ABC'}}]
Optional[Insurance{name='ABC'}]
Optional[ABC]
ABC
  • 测试2:Person和Car不为空,Insurance为空:
        Car car2 = new Car(null);Person person2 = new Person(car2);Optional<Person> optPerson2 = Optional.ofNullable(person2);System.out.println(optPerson2.flatMap(Person::getCar));System.out.println(optPerson2.flatMap(Person::getCar).flatMap(Car::getInsurance));System.out.println(optPerson2.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson2.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional[Car{insurance=null}]
Optional.empty
Optional.empty
Unknown
  • 测试3:Person不为空,Car和Insurance为空:
        Person person3 = new Person(null);Optional<Person> optPerson3 = Optional.ofNullable(person3);System.out.println(optPerson3.flatMap(Person::getCar));System.out.println(optPerson3.flatMap(Person::getCar).flatMap(Car::getInsurance));System.out.println(optPerson3.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson3.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional.empty
Optional.empty
Optional.empty
Unknown
  • 测试4:Person,Car,Insurance都为空:
        Optional<Person> optPerson4 = Optional.ofNullable(null);System.out.println(optPerson4.flatMap(Person::getCar));System.out.println(optPerson4.flatMap(Person::getCar).flatMap(Car::getInsurance));System.out.println(optPerson4.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName));System.out.println(optPerson4.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown"));

运行结果如下:

Optional.empty
Optional.empty
Optional.empty
Unknown

总结

Optional 类帮助我们处理可能存在的null值。它的用法类似于流(Stream),简单明了,可以精简代码,提高代码可读性和可维护性。

Optional的常用方法如下:

方法描述
empty()返回一个空的Optional实例
filter()类似流的filter
flatMap()类似流的flatMap
get()获取封装的对象,若其为空,则抛出NoSuchElementException
ifPresent()如果值存在,则运行传入的Consumer
isPresent()值是否存在
map()类似流的map
of()返回封装指定值的Optional对象,若指定值为null,则抛出NPE
ofNullable()同of(),但允许null值
orElse()获取封装的对象,若其为空,则返回指定值
orElseGet()获取封装的对象,若其为空,则运行传入的Supplier并返回其结果
orElseThrow()获取封装的对象,若其为空,则运行传入的Supplier并抛出其生成的异常

参考

  • https://livebook.manning.com/book/java-8-in-action/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/146561.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

2024/9/21 英语每日一段

“Girls mature earlier than boys,” she says. “They hit rapid growth at 11, 12, whereas boys hit it about 13. Once you hit ‘peak height velocity’, training should concentrate on the structural side, push strength and muscle development. Girls are missin…

部标(JT/T1078)流媒体对接说明

1.前言 最近在配合客户开发流媒体相关的服务的时候&#xff0c;整理了一些对接过程资料&#xff0c;这里做个分享与记录。流媒体的对接主要牵扯到4个方面&#xff1a; &#xff08;1&#xff09;平台端&#xff1a;业务端系统&#xff0c;包含前端呈现界面。 &#xff08;2&a…

Transformer宝藏入门教程,五天肝疯了—Transformer最全面的入门指南

随着 BERT、GPT 等大规模语言模型的兴起&#xff0c;越来越多的公司和研究者采用 Transformers 库来构建 NLP 应用。本文档教程里包括了自然语言处理、Transformer模型、注意力机制、pytorch、微调预训练模型、翻译任务、序列标注任务、文本摘要等等模块 一、内容介绍 《Tran…

【正点原子K210连载】第三十九章 YOLO2人脸检测实验 摘自【正点原子】DNK210使用指南-CanMV版指南

第三十九章 YOLO2人脸检测实验 从本章开始&#xff0c;将通过几个实例介绍Kendryte K210上的KPU&#xff0c;以及CanMV下KPU的使用方法&#xff0c;本章将先介绍YOLO2网络的人脸检测应用在CanMV上的实现。通过本章的学习&#xff0c;读者将学习到YOLO2网络的人脸检测应用在Can…

数字人直播带货火了,只要有了这个工具,就可以打造数字人,建议新手小白赶紧尝试!

经济下行&#xff0c;普通人应该尽早认清一个事实&#xff0c;没有一技之长&#xff0c;没有核心竞争力&#xff0c;即便是打工皇帝&#xff0c;年入百万也只是浮云。 一定要保证主业的稳定&#xff0c;再探索新的机会&#xff0c;要多从”1-10"&#xff0c;而不是反复”…

2024/9/21黑马头条跟学笔记(十)

1&#xff09;今日内容 1.1&#xff09;定时计算流程 1.不想用户看到的全是最新的&#xff0c;实时计算最火的推送 2.定时计算热度最高&#xff0c;存redis&#xff0c;推送到推荐页面 1.2&#xff09;使用schedule 多个服务部署&#xff0c;多次执行 硬编码定时时间在cron…

群晖使用Docker部署WPS Office并实现异地使用浏览器制作办公文档

文章目录 前言1. 本地环境配置2. 制作本地分享链接3. 制作公网访问链接4. 公网ip地址访问您的分享相册5. 制作固定公网访问链接 前言 想象一下这个场景&#xff1a;如果遇到周末紧急需要改方案&#xff0c;但团队成员都在各自家中&#xff0c;这个时候如果大家能够轻松访问这个…

海洋大地测量基准与水下导航系列之二国外海底大地测量基准和海底观测网络发展现状(上)

海底大地控制网建设构想最先由美国斯克里普斯海洋研究所(Scripps Institution of Oceanography,SIO)提出&#xff0c;目前仅有少数发达国家具备相应技术条件。美国、日本、俄罗斯和欧盟等发达国家通过布测先进的海底大地控制网&#xff0c;不断完善海洋大地测量基准基础设施&am…

SpringCloud Alibaba五大组件之——Sentinel

SpringCloud Alibaba五大组件之——Sentinel&#xff08;文末附有完整项目GitHub链接&#xff09; 前言一、什么是Sentinel二、Sentinel控制台1.下载jar包2.自己打包3.启动控制台4.浏览器访问 三、项目中引入Sentinel1.在api-service模块的pom文件引入依赖&#xff1a;2.applic…

一堆让你眼界大开的实用工具网站——搜嗖工具箱

和图书 https://www.hetushu.com/ 一个好用的免费看小说网站。和图书是一个提供各种热门电子书,书籍,小说免费在线阅读的网站&#xff0c;涵盖网游、玄幻、穿越、科幻、仙侠、都市、武侠、历史、竞技、军事灵异等多个种类的小说。在这个网站看小说最大的感触简单干净&#xff…

C++速通LeetCode中等第15题-搜索二维矩阵II(两种方法)

方法一&#xff1a;二分法按行遍历查找&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {for (const auto& row: matrix) {auto it lower_bound(row.begin(), row.end(), target);if (it ! row.end()…

【C++掌中宝】在正式学习C++之前,你还应该了解哪些东西?

文章目录 前言1. C发展历史1.1 C版本更新1.2 关于C23的一个小故事 2. C参考文档3. C的重要性3.1 编程语言排行榜3.2 C在工作领域中的应用 4. C学习建议和书籍推荐4.1 C学习难度4.2 C学习建议4.3 学习书籍推荐 5. C第一个程序结语 前言 在正式学习C之前&#xff0c;我觉得应该先…

Linux:make,Makefile

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《Linux&#xff1a;make&#xff0c;Makefile》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&…

Java面试篇-AOP专题(什么是AOP、AOP的几个核心概念、AOP的常用场景、使用AOP记录操作日志、Spring中的事务是如何实现的)

文章目录 1. 什么是AOP2. AOP的几个核心概念3. AOP的常用场景4. 使用AOP记录操作日志4.1 准备工作4.1.1 引入Maven依赖4.1.2 UserController.java4.1.3 User.java4.1.4 UserService.java 4.2 具体实现&#xff08;以根据id查询用户信息为例&#xff09;4.2.1 定义切面类&#x…

基于uni-app的计算机类面试宝设计与实现(毕业论文)

目 录 1 前言 1 1.1 研究目的与意义 1 1.2 研究现状 1 1.3 论文结构 2 2 可行性分析 3 2.1 经济可行性 3 2.2 法律可行性 3 2.3 技术可行性 4 2.4 市场可行性 4 2.5 可行性分析结论 4 3 系统需求分析 4 3.1 用户需求分析 4 3.2 系统功能分析 5 3.3 系统性能需求分析 6 4 概要设…

【网络安全 | 靶机搭建】修改镜像源、更新软件源、安装git、更改python版本等

文章目录 0x00、必要准备0x01、修改镜像源0x02、更新软件源并清除缓存0x03、安装git0x04、更改默认Python版本为python30x05、安装增强功能0x06、vmware虚拟机导出iso0x00、必要准备 安装虚拟机时必须保存用户名、密码,用于后续操作,可以截图保存: 以下内容按个人需要进行配…

重生奇迹MU 强化玩法套路多 极品装备由你打造

欢迎来到重生奇迹MU的强化玩法指南&#xff01;想要打造极品装备吗&#xff1f;不可错过这篇文章&#xff0c;我们将为您揭开最多套路的强化技巧和窍门&#xff0c;帮您节省时间和资源&#xff0c;并带来最高效的升级结果。无论您是新手还是老玩家&#xff0c;本文适合所有级别…

AI浪潮新崛起:借助AI+实景/视频直播创新魅力,开启无人自动直播新时代!

AI浪潮新崛起&#xff1a;借助AI实景/视频直播创新魅力&#xff0c;开启无人自动直播新时代&#xff01; 在科技日新月异的今天&#xff0c;人工智能&#xff08;AI&#xff09;已不再仅仅是科幻电影中的桥段&#xff0c;它正以不可阻挡之势渗透到我们生活的方方面面&#xff…

工业物联网的海量数据如何呈现,可视化设计来助力

工业物联网产生的海量数据需要通过可视化设计来呈现&#xff0c;以帮助用户更好地理解和分析数据。 数据汇总和聚合&#xff1a; 对于大量的数据&#xff0c;可以通过汇总和聚合的方式来减少数据的数量&#xff0c;同时保留关键的信息。例如&#xff0c;将时间序列数据按照小…

Excel 冻结多行多列

背景 版本&#xff1a;office 2021 专业版 无法像下图内某些版本一样&#xff0c;识别选中框选的多行多列。 如下选中后毫无反应&#xff0c;点击【视图】->【冻结窗口】->【冻结窗格】后自动设置为冻结第一列。 操作 如下&#xff0c;要把前两排冻结起来。 选择 C1&a…