Java8的Optional简介

news2024/11/15 17:20:05

文章目录

  • 环境
  • 背景
    • 方法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 null
	at 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.coloradmin.cn/o/2142587.html

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

相关文章

samba配置

首先需要在linux上使用命令sudo apt install samba安装samba功能&#xff0c;安装之后&#xff0c;使用命令sudo smbpasswd -a zxy添加samba的密码&#xff0c;我这里使用的是和ubuntu的登陆密码&#xff0c;然后需要编辑samba相关的功能&#xff0c;使用命令sudo vim /etc/sam…

通信工程学习:什么是AN-SMF接入网系统管理功能

AN-SMF接入网系统管理功能 AN-SMF&#xff08;Access Network System Management Function&#xff0c;即接入网系统管理功能&#xff09;是通信网络中接入网&#xff08;AN&#xff09;的一个重要组成部分&#xff0c;主要负责协调和管理接入网内的各种功能和资源。以下是对AN…

【Android Studio】app:compileDebugJavaWithJavac FAILED解决办法

文章目录 问题描述解决办法 问题描述 Task :app:compileDebugJavaWithJavac FAILED The following annotation processors are not incremental: jetified-butterknife-compiler-10.0.0.jar (com.jakewharton:butterknife-compiler:10.0.0). Make sure all annotation processo…

启动windows更新/停止windows更新,电脑自动更新怎么彻底关闭?如何操作?

关于启动Windows更新、停止Windows更新以及彻底关闭电脑自动更新的问题&#xff0c;以下是根据专业角度提供的详细指导&#xff1a; 启动Windows更新 1.通过Windows设置启动更新&#xff1a; -点击开始菜单&#xff0c;选择“设置”&#xff08;或使用快捷键WinI&a…

主流的Java的webapi接口模板特点分析

Java 作为一种广泛应用于企业级开发的编程语言&#xff0c;其在 Web API 开发中具有重要的地位。随着 Java 生态系统的不断发展&#xff0c;市面上涌现了多种不同的 Web API 框架和设计模式。不同的 Web API 模板在设计上各有特点&#xff0c;适合不同类型的开发需求。本文将详…

明月皎皎,思念悠悠 孙滢迎深情演唱《月亮与笆篓》

明月皎皎&#xff0c;思念悠悠 孙滢迎深情演唱《月亮与笆篓》 在中秋佳节的月光下&#xff0c;一首满含对妈妈思念的歌曲《月亮与笆篓》&#xff0c;如同一缕轻柔的晚风&#xff0c;悄然拂过心间&#xff0c;勾起无尽的回忆与眷恋。 该歌曲由全国机关事务管理研究会。徐进作词…

2022高教社杯全国大学生数学建模竞赛C题 问题一(3) Python代码

目录 1.3 根据风化点检测数据,预测其风化前的化学成分含量数据重塑数据可视化回归随机森林回归XGboost回归Gradient Boosting回归LightGBM回归CatBoost回归决策树回归MLP回归预测数据复原1.3 根据风化点检测数据,预测其风化前的化学成分含量 数据重塑 import numpy as npdf=…

『功能项目』第二职业法师的平A【57】

我们打开上一篇56制作提示主角升级面板的项目&#xff0c; 本章要做的事情是制作法师平A的魔法球触碰到Boss后让Boss受到一个无视攻击力与防御力的一个&#xff08;100&#xff09;左右随机的一个伤害值 修改脚本&#xff1a;PlayerCtrl.cs 将法师职业生成的魔法球的标签Tag设…

Java4----String

一、概述 java.lang.String 类代表字符串&#xff0c;Java程序中所有字符串文字都为此类 注意&#xff1a; 字符串内容是不会发生改变的&#xff0c;它的对象在创建后不能被更改 比如&#xff1a; String name "666"; name "8wx"; 这是两个字符串 java程…

通信工程学习:什么是HFC混合光纤同轴电缆

HFC&#xff1a;混合光纤同轴电缆 HFC&#xff08;Hybrid Fiber Coaxial&#xff09;混合光纤同轴电缆是一种结合了光纤与同轴电缆的宽带接入网技术。以下是对HFC混合光纤同轴电缆的详细解释&#xff1a; 一、HFC混合光纤同轴电缆的定义与概述 定义&#xff1a;HFC是一种结合光…

如何设置开机跳过桌面启动自己的应用及如何恢复

开机跳过桌面启动自己的应用 最近研究如何在win10下实现kiosk模式&#xff0c;其实win10本身是有一个展台模式&#xff0c;就是可以把应用设为window启动后进入的一个程序且全屏显示&#xff0c;用户无法切换到桌面或其他应用&#xff0c;但它的前提是只能从UWP的应用里去选择&…

利士策分享,中秋致敬:向坚守一线的劳动者致以最深敬意

利士策分享&#xff0c;中秋致敬&#xff1a;向坚守一线的劳动者致以最深敬意 在这个月华如练的夜晚&#xff0c;当万家灯火与皎洁的明月交相辉映&#xff0c;家家户户围坐一堂&#xff0c;共享中秋团圆的温馨时刻&#xff0c;有那么一群人&#xff0c;他们的身影却依然忙碌在…

Python | python中的特殊方法__str__和__repr__

__str__和__repr__ 无方法有方法__str____repr__同时存在 __str__和__repr__都是更改print的输出形式 无方法 无特殊方法 class Person:def __init__(self,name,age):self.name nameself.age ageprint(Person(aa, 34))<main.Person object at 0x000002231EF78B38> …

uniapp 如何自定义导航栏并自适应机型

如今的移动设备有各种不同的屏幕形状&#xff0c;如刘海屏、水滴屏等。这些异形屏会影响页面的布局&#xff0c;尤其是导航栏和底部栏的显示。通过获取安全区域信息&#xff0c;可以确保页面内容不会被异形屏的特殊区域遮挡。 在设计页面顶部导航栏时&#xff0c;可以根据 saf…

springboot 集成轻量级规则编排引擎 LiteFlow 使用详解

目录 一、前言 二、流程编排概述​​​​​​​ 2.1 什么是流程编排 2.1.1 流程编排主要特征 2.1.2 流程编排应用场景 2.2 流程编排与工作流区别 2.2.1 定义上的差别 2.2.2 应用场景上的差别 2.2.3 技术实现上的差异 三、微服务中流程编排常用的技术 3.3.1 LiteFlow …

车型展示+接驳体验!苏州金龙海格客车闪耀汉诺威商用车展

德国当地时间9月16日&#xff0c;IAA汉诺威商用车展媒体日活动在德国汉诺威展览中心开幕。该展会自1897年首次举办以来&#xff0c;已有超过一个世纪的历史&#xff0c;是全球历史最长、规模最大、最具影响力的专业商用车展之一&#xff0c;更是世界商用车行业技术创新和发展趋…

【Java宝典】——探索数组的奥秘之旅

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” 文章目录 Java数组常见报错①:ArrayIndexOutOfBoundsException(数组索引超出范围)②:NullPointerException(空…

【数字组合】

题目 思路 状态表示&#xff1a; f [ i ] [ j ] f[i][j] f[i][j] 对应考虑1到 i 号数字&#xff0c;和为 j 的方法&#xff0c;表示方法数 目标表示&#xff1a; f [ n ] [ m ] f[n][m] f[n][m] 状态转移&#xff1a; f [ i ] [ j ] f [ i − 1 ] [ j ] f [ i − 1 ] [ j …

2022高教社杯全国大学生数学建模竞赛C题 问题二(1) Python代码

目录 问题 22.1 依据附件数据分析高钾玻璃、铅钡玻璃的分类规律数据类别编码不平衡数据处理分类模型决策树分类随机森林分类XGBoost分类LightGBM分类Catboost分类基于直方图的梯度提升Histogram-Based Gradient Boosting梯度提升树Gradient Boosting Tree逻辑回归Logistic朴素贝…

Python | Leetcode Python题解之第409题最长回文串

题目&#xff1a; 题解&#xff1a; class Solution:def longestPalindrome(self, s: str) -> int:ans 0count collections.Counter(s)for v in count.values():ans v // 2 * 2if ans % 2 0 and v % 2 1:ans 1return ans