Java8中Optional类入门-替代null避免冗杂的非空校验

news2025/1/11 7:52:23

场景

Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验:

Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验_霸道流氓气质的博客-CSDN博客

上面在讲Guava时讲过Optional,下面做具体的入门示例讲解。

构建一个关系

public class Insurance {
    private String name;

    public String getName() {
        return name;
    }
}


public class Car {
    private Insurance insurance;

    public Insurance getInsurance() {
        return insurance;
    }
}

public class Person {
    private Car car;

    public Car getCar() {
        return car;
    }
}

人-购买车-车买保险的关系类如上。

如果需要获取保险的名称可能需要做多重非空校验,如果person不为空,则调用person的getCar获取car对象,

如果car不为空,获取car的保险insurance,如果保险不为空,则获取保险名称。

注:

博客:
https://blog.csdn.net/badao_liumang_qizhi 

实现

1、Java 8中引入了一个新的类java.util.Optional<T>。这是一个封装Optional值的类 。

变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,
由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。

        //一 、创建Optional 对象
        //1、声明一个空的Optional
        Optional<Object> empty = Optional.empty();
        //2、依据一个非空值创建Optional
        Car car = new Car();
        Optional<Object> o = Optional.of(car);
        //3、可接受null的Optional
        Optional<Object> o1 = Optional.ofNullable(null);

2、使用map从Optional对象中提取和转换值

把上面新建的三个类分别修改为

public class Insurance {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

import java.util.Optional;

public class Car {
    private Optional<Insurance> insurance;

    public Optional<Insurance> getInsurance() {
        return insurance;
    }

    public void setInsurance(Optional<Insurance> insurance) {
        this.insurance = insurance;
    }
}

import java.util.Optional;

public class Person {
    private Optional<Car> car;

    public Optional<Car> getCar() {
        return car;
    }

    public void setCar(Optional<Car> car) {
        this.car = car;
    }
}

这里不对保险进行修改,是因为在业务上认为保险一定是有名字,但是车不一定买保险,人不一定买车。

Optional提供了一个map方法

        Insurance insurance = new Insurance();
        insurance.setName("badao");

        Optional<Insurance> insurance1 = Optional.ofNullable(insurance);
        Optional<String> name = insurance1.map(Insurance::getName);
        System.out.println(name.get());//badao

        Optional<Insurance> insurance2 = Optional.ofNullable(null);
        Optional<String> name2 = insurance2.map(Insurance::getName);
        System.out.println(name2);//Optional.empty

这里的map与流的map方法相差无几。map操作会将提供的函数应用于流的每个元素。

可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。

如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。

如果Optional为空,就什么也不做。

3、使用flatMap链接Optional对象

如果是需要链接Optional对象时需要使用flatMap,比如

        Car car1 = new Car();
        car1.setInsurance(Optional.of(insurance));
        Person person = new Person();
        person.setCar(Optional.of(car1));
        Optional<Person> person1 = Optional.of(person);

        //处理潜在可能缺失的值时,使用Optional具有明显的优势。
        // 这一次,你可以用非常容易却又普适的方法实现之前你期望的效果——不再需要使用那么多的条件分支,也不会增加代码的复杂性。
        String s = person1
                .flatMap(Person::getCar)
                .flatMap(Car::getInsurance)
                .map(Insurance::getName)
                .orElse("默认名字");
        System.out.println(s);

4、操纵由Optional对象构成的Stream

比如要找出person列表中所使用的保险公司名称(不含重复项)

构造一波模拟数据

        Insurance insurance1 = new Insurance();
        insurance1.setName("badao");
        Car car1 = new Car();
        car1.setInsurance(Optional.of(insurance1));
        Person person1 = new Person();
        person1.setCar(Optional.of(car1));

        Insurance insurance2 = null;
        Car car2 = new Car();
        car2.setInsurance(Optional.ofNullable(insurance2));
        Person person2 = new Person();
        person2.setCar(Optional.of(car2));

        Car car3 = null;
        Person person3 = new Person();
        person3.setCar(Optional.ofNullable(car3));

        ArrayList<Person> personArrayList = new ArrayList<Person>() {
            {
                this.add(person1);
                this.add(person2);
                this.add(person3);
            }
        };

实现

        Stream<Optional<String>> optionalStream = personArrayList.stream()
                .map(Person::getCar)
                .map(car -> car.flatMap(Car::getInsurance))
                .map(insurance -> insurance.map(Insurance::getName));

上面三个操作得到了一个Stream<Optional<String>>对象,这些Optional对象中的一些可能为空,因为有的人可能并没有汽车,

或者有汽车但是没有投保。使用Optional,即便是碰到了值缺失的情况,你也不需要再为这些操作是否“空安全”(null-safe)

而烦心了。然而,你现在碰到了新的问题,怎样去除那些空的Optional对象,解包出其他对象的值,并把结果保存到集合Set中。

        //去除空的对象
        Set<String> collect = optionalStream.filter(Optional::isPresent)
                .map(Optional::get)
                .collect(Collectors.toSet());
        System.out.println(collect);

各阶段的类型明细为

 

5、两个Optional对象的组合

假设有这样一个方法,它接受一个Person和一个Car对象,并以此为条件对外部提供的服务进行查询,

通过一些复杂的业务逻辑,试图找到满足该组合的最便宜的保险公司。

    public Insurance findCheapestInsurance(Person person,Car car){
        return new Insurance();
    }

需要接收两个Optional对象作为参数,返回值是一个Optional<Insurance>对象,如果传入的任何一个参数值为空,

它的返回值亦为空。

    public Optional<Insurance> nullSafeFindInsurance1(Optional<Person> person,Optional<Car> car){
        if(person.isPresent() && car.isPresent()){
            return Optional.of(findCheapestInsurance(person.get(),car.get()));
        }else{
            return Optional.empty();
        }
    }

其实还可以用如下一行语句实现

    public Optional<Insurance> nullSafeFindInsurance2(Optional<Person> person,Optional<Car> car){
        return person.flatMap(person1 -> car.map(car1 -> findCheapestInsurance(person1,car1)));
    }

对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它的Lambda表达式就不会执行,

这次调用会直接返回一个空的Optional对象。反之,如果person对象存在,这次调用就会将其作为函数Function的输入,

并按照与flatMap方法的约定返回一个Optional<Insurance>对象。这个函数的函数体会对第二个Optional对象执行map操作,

如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个nullSafeFindCheapestInsurance方法的返回值

也是一个空的Optional对象。最后,如果person和car对象都存在,那么作为参数传递给map方法的Lambda表达式就能

够使用这两个值安全地调用原始的findCheapestInsurance方法。

6、使用filter剔除特定的值

经常需要调用某个对象的方法,查看它的某些属性。 比如,你可能需要检查保险公司的名称是否为“badao”。
为了以一种安全的方式进行这些操作,你首先需要确定引用指向的Insurance对象是否为null,之后再调用它的getName方法。

        Insurance insurance1 = new Insurance();
        insurance1.setName("badao");
        if(insurance1 !=null && "badao".equals(insurance1.getName())){
            System.out.println("ok");
        }

可以将其使用filter重构

        Optional<Insurance> insurance11 = Optional.of(insurance1);
        insurance11.filter(insurance -> "badao".equals(insurance.getName()))
                .ifPresent(x -> System.out.println("ok"));

7、Optional类的方法

 方法 描述
 empty 返回一个空的Optional实例
 filter 如果值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的Optional对象
 flatMap 如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象
 get 如果值存在,就将该值用Optional封装返回,否则抛出一个NoSuchElementException异常
 ifPresent 如果值存在,就执行使用该值的方法调用,否则什么也不做
 ifPresentOrElse 如果值存在,就以值作为输入执行对应的方法调用,否则执行另一个不需任何输入的方法
 isPresent 如果值存在就返回true,否则返回false
 map 如果值存在,就对该值执行提供的mapping函数调用
 of 将指定值用Optional封装之后返回,如果该值为null,则抛出一个NullPointerException异常
 ofNullable 将指定值用Optional封装之后返回,如果该值为null,则返回一个空的Optional对象
 or 如果值存在,就返回同一个Optional对象,否则返回由支持函数生成的另一个Optional对象
 orElse 如果有值则将其返回,否则返回一个默认值
 orElseGet 如果有值则将其返回,否则返回一个由指定的Supplier接口生成的值
 orElseThrow 如果有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常
 stream 如果有值,就返回包含该值的一个Stream,否则返回一个空的Stream

8、Optional两个使用示例

避免如果Map中不含指定的键对应的值,它的get方法就会返回一个null

 Optional<Object> aa = Optional.ofNullable(map.get("aa"));

Integer.parseInt(String),将String转换为int。在这个例子中,如果String无法解析到对应的整型

该方法就抛出一个NumberFormatException。最后的效果是,发生String无法转换为int时,代码发出一个遭遇非法参数的信号,

唯一的不同是,这次你需要使用try/catch语句,而不是使用if条件判断来控制一个变量的值是否非空

    public static Optional<Integer> stringToInt(String s)
    {
        try{
            //如果String能转换为对应的Integer,将其封装在Optional对象中返回
            return Optional.of(Integer.parseInt(s));
        }catch (NumberFormatException e){
            //否则返回一个空的Optional对象
            return Optional.empty();
        }
    }

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

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

相关文章

深度学习笔记:python的numpy和matplotlib库

1 numpy库 numpy为python数学计算库&#xff0c;里面的数组类提供大量便捷的数组和矩阵运算方法 创建numpy数组&#xff1a; import numpy as np x np.array([1.0, 2.0, 3.0])创建二维数组&#xff1a; import numpy as np a np.array([[1, 2], [3, 4]]) a.shape # outpu…

bWAPP靶场搭建——直接使用虚拟机镜像导入配置

一、bWAPP简介 bwapp是一款非常好用的免费的、开源漏洞演示学习平台;它有100多个网络错误&#xff01;且它涵盖了所有已知的主要web漏洞&#xff0c;包括OWASP Top 10项目的所有风险。 bWAPP是一个使用MySQL数据库的PHP应用程序&#xff1b;它可以通过Apache/IIS和MySQL托管在L…

优维低代码:关联微应用和Feature Flags 特性开关

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 连载…

图论(1):单源最短路的建图方式

一、单源最短路算法 最短路算法_yan__kai_的博客-CSDN博客 二、例题 1.acwing1129 题意解读&#xff1a;走过一条路存在花费c&#xff0c;求最小费用即求最短路。无向图 直接背模板&#xff1a; #include<iostream> #include<algorithm> #include<cstring&…

环形轨道运料电动葫芦无线控制系统的组成与功能

一&#xff0e;关于环形轨道运料电动葫芦的控制 目前电动葫芦的控制部分都是由PLC完成的&#xff0c;每个电葫芦内都有一台PLC&#xff0c;主站PLC一般设置在地面控制机柜内。由于电葫芦是在半空中移动工作的&#xff0c;与地面PLC的通讯问题一直是电葫芦设备运行中棘手的问题…

虚拟人,会成为品牌下一次逆势的解药吗?

如果说2021年是虚拟偶像的崛起元年&#xff0c;那2022可以称得上是爆发期了&#xff0c;一方面&#xff0c;定位国风、时尚博主、模特、歌手的虚拟偶像数量激增&#xff0c;被冠以“首位”、“行业第一”等称号的新面孔轮番出道&#xff1b;其次&#xff0c;虚拟人与品牌的商业…

不同Excel多列对比记录新增、修改、删除和无变化的行,并生成对比报告

执行【Exce数据对比.exe】&#xff0c;打开界面如图选择要对比的Excel文件和被对比的Excel文件&#xff0c;输入要对比Sheet页的需要&#xff08;注意&#xff0c;Sheet需要从0开始&#xff09;输入主键列和被对比的列。主键列和被对比列均为二维列表&#xff0c;列的索引从0开…

七、HashSet底层详解

文章目录特点结论源码解读构造器添加元素小结说明练习(重要*掌握)思考特点 无序、无索引 不可重复(地址)&#xff0c;可存一个null 不可用索引取出 存放和取出顺序不一定一样 但每次取出的顺序是一样的 遍历只能迭代器和增强for 底层其实是HashMap 结论 源码解读 构造…

Spring AOP源码:配置文件解析过程

前言 本篇文章主要讲解AOP配置中的几个通知类的解析过程&#xff0c;为后续对目标类进行代理做准备&#xff1b;在前面的Spring IOC篇我们讲解了自定义配置的解析&#xff0c;AOP配置的解析过程也是其自定义注解的过程&#xff0c;如果不熟悉自定义解析过程可以看之前的文章Sp…

CVPR2017|Deep Feature Flow for Video Recognition论文复现(pytorch版)

&#x1f3c6;引言&#xff1a;深度卷积神经网络在图像识别任务中取得了巨大的成功。然而&#xff0c;将最先进的图像识别网络转移到视频上并非易事&#xff0c;因为每帧评估速度太慢且负担不起。我们提出了一种快速准确的视频识别框架——深度特征流DFF。它只在稀疏关键帧上运…

数据通信基础 - 调制技术

文章目录1 概述2 调制技术2.1 分类2.2 N 相调制3 网工软考真题1 概述 #mermaid-svg-ZTF6pPysJlmUes01 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-ZTF6pPysJlmUes01 .error-icon{fill:#552222;}#mermaid-svg-ZTF…

谷歌用量子处理器发现:光子能在混沌中保持稳健的束缚态

一圈超导量子比特可以容纳微波光子的“束缚态”&#xff0c;其中光子往往聚集在相邻的量子比特位点上。图片来源&#xff1a;Google Quantum AI 使用量子处理器&#xff0c;研究人员可以使微波光子具有异常的“粘性”。在诱使它们聚集成束缚态后&#xff0c;他们发现这些光子簇…

谷歌 Chrome 浏览器弹窗境外广告的解决方法

谷歌的 Chrome 浏览器是我非常喜欢的一款的浏览器&#xff0c;用了它之后就不想再用其它浏览器。可是不知道从什么时候开始&#xff0c;Chrome 浏览器居然时不时地在右下角弹出广告&#xff0c;仔细一看&#xff0c;还是境外的 VPN 广告&#xff0c;如下图。有弹出过几次了&…

如何通过创建 SSH key 来进行Git 代码管理

1.检查你的电脑是否已经有SSH Key&#xff1f; 运行如下命令查看&#xff1a; $ cd ~/.ssh $ ls如果存在id_rsa.pub或 id_dsa.pub 文件&#xff0c;说明你的电脑已经有 SSH Key &#xff0c;可以直接拿来用&#xff0c;如果没有的话需要创建。 2.创建SSH Key 配置全局的nam…

zookeeper入门篇

文章目录前言介绍安装与启动配置说明节点节点类型PERSISTENT&#xff08;持久化节点&#xff09;PERSISTENT_SEQUENTIAL&#xff08;持久化顺序节点&#xff09;EPHEMERAL&#xff08;临时节点&#xff09;EPHEMERAL_SEQUENTIAL&#xff08;临时顺序节点&#xff09;Container&…

用Java实现简单的图书管理系统(Java系列7)

目录 前言&#xff1a; 1.基础框架的搭建 1.1图书 1.1.1书 1.1.2书架 1.2用户 1.2.1抽象类 1.2.2普通用户 1.2.3管理员 1.3操作 1.3.1新增图书 1.3.2借阅图书 1.3.3删除图书 1.3.4退出图书 1.3.5查找图书 1.3.6归还图书 1.3.7显示图书 2.具体内容的实现 2.1Ma…

<flutter>跨平台开发新手入坑指南 dart dio pubspec.yaml json_annotation 打包 小坑指南

1.资源文件和依赖三方包&#xff08;pubspec.yaml&#xff09;&#xff1a; pubspec.yaml文件可以说是和安卓的gradle文件差不多&#xff0c;它用来描述版本号、sdk、依赖等的。 在资源导入方面同安卓不一样的是&#xff0c;flutter需要在pubspec.yaml中声名&#xff0c;不然…

【PCB专题】Allegro元件库路径设置方法

正常Layout拉线前,需要将原理图导出的网表导入到Allegro里,Allegro就会自动将元件导入。如果库路径没有设置或都软件找不到器件,将会非常的卡顿,并且报Completed with warnings/errors。如下图所示: 在弹出的错误报告View of file:netrev.lst中会提示很多器件找不到封装。…

js知识点

js有预解析阶段&#xff0c;变量声明提升只提升定义&#xff0c;不提升值 console.log(a);//undefined var a10; 基本数据类型 Number、String、Boolean、Undefined和Null 复杂数据类型 Object、Array、Function、RegExp、Date、Map、Set等 使用typeof运算符可以检测值或…