java中Optional的应用,以及map和flatMap的区别

news2024/11/19 3:17:40

关于Option的介绍可以看深入理解java8中的Optional 类就可以了,但是复杂一点的使用在网上却没有搜到,这里结合我开发时遇到的真实案例来讲一下Option的使用。

1.案例一

        在真实业务操作过程中,都是对象里面套对象,这边先简单定义操作对象:

public class PictureCondition {
    private String url;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

}

       在这里先一个简单的对象,里面也就只有一个属性,在Controller层需要接收一个PictureCondition的对象,应判断这个对象里面的url属性是否为空,或者是有空格出现,如果有的话,则抛出一个异常。

      使用一般的写法是这样判断的:

if(pictureCondition==null||pictureCondition.getUrl()==null||"".equals(pictureCondition.getUrl().trim())){
            throw new RuntimeException("出错啦");
 }

      使用Option的写法是这样的:

Optional.ofNullable(pictureCondition)
                .map(PictureCondition::getUrl)
                .map(String::trim)
                .filter(x -> !"".equals(x))
                .orElseThrow(() -> new RuntimeException("出错啦"));

      对于单层循环使用一般的写法还是可以应对的,但是当层数比较多时,这样写就不太好了。

2.案例二

先定义操作对象

1.定义User对象

public class User {
    //用户名称
    private String username;

    //当前用户的订单信息
    private List<Order> orders;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public List<Order> getOrders() {
        return orders;
    }

    public void setOrders(List<Order> orders) {
        this.orders = orders;
    }
}

2.定义订单对象

public class Order {
    //订单id
    private Long orderId;

    //订单时间
    private Date createTime;

    public Long getOrderId() {
        return orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }

    public Date getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
}

具体场景:

        当在一个方法里面需要拿到user对象(可能为空)里面是的orders对象,如果当前orders对象为空,则返回一个空的list对象。

写法:

        Java8之前的写法,我们需要先判断user对象是否为null,不为空的话,获取当orders对象是否为空,为空则返回一个空的list

if(user!=null && user.getOrders!=null) {
  return user.getOrders();
} else {
  return Collections.emptyList();
}

        使用Java8的写法,那么就不需要这样判断了,直接看代码:

return Optional.ofNullable(user)
        .map(u -> u.getOrders())
        .orElse(Collections.emptyList())
 

扩展思考:

        Optional.map 是可以无限级联的, 比如再深一层, 获得用户名的大写形式

return Optional.ofNullable(user)
           .map(u -> u.getUsername())
           .map(name -> name.toUpperCase())
           .orElse(null);

这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断

User user = .....
if(user != null) {
  String name = user.getUsername();
  if(name != null) {
    return name.toUpperCase();
  } else {
    return null;
  }
} else {
  return null;
}

3.Optional中的map和flatMap的区别

源码:

map会将传入的Function函数的结果进行封装,先看源码:

    public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Optional.ofNullable(mapper.apply(value));//会使用Optional的ofNullable方法包装Function函数返回的值
        }
    }

flatMap会直接返回Function函数执行的结果,看源码:

    public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
        Objects.requireNonNull(mapper);
        if (!isPresent())
            return empty();
        else {
            return Objects.requireNonNull(mapper.apply(value));//直接返回Function执行的结果
        }
    }

      这样看,好像也看不出来两者太大的区别,不知道什么时候用map,什么时候用flatMap,通过下面的案例进行具体分析

map和flatMap的用法区别

首先对于下列对象的操作

public class School {
    private String name;
    private Optional<Tearch> tearch;  //属性为Option封装的Tearch对象

    public String getName() {
        return name;
    }

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

    public Optional<Tearch> getTearch() {
        return tearch;
    }

    public void setTearch(Optional<Tearch> tearch) {
        this.tearch = tearch;
    }
}

class Tearch{
    private String name;
    private Optional<Student> student;

    public String getName() {
        return name;
    }

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

    public Optional<Student> getStudent() {
        return student;
    }

    public void setStudent(Optional<Student> student) {
        this.student = student;
    }
}

class Student{
    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

   这时,如果给你一个School对象,让你得到Student的name属性的值,可以使用下面的方式:

public static String getStudentName(School school){
        return Optional.ofNullable(school)
                .map(School::getTearch)
                .map(Tearch::getStudent)
                .map(Student::getName)
                .orElse("false");
    }

 你可能感觉这样写很对啊,没毛病,可惜这段代码连编译都不会通过的,我们思考一下,School::getTearch会返回School实例的tearch属性,而tearch属性是使用Optional包装的Tearch对象,所以使用了map(School::getTearch),会返回Optional<Optional<Tearch>>对象,而不是我们所想的Optional<Tearch>,这是你可能想自己试一下,我已经做了截图,毕竟有图有真相。

20191119203530875.png

2019111920380750.png

这时就可以使用flatMap来解决这个问题,刚才已经说了,flatMap不会使用Optional包装Function执行的返回结果,所以我们可以使用flatMap来解决这个问题。

    public static String getStudentName(School school){
        return Optional.ofNullable(school)
                .flatMap(School::getTearch)
                .flatMap(Tearch::getStudent)
                .map(Student::getName).orElse("false");
    }

看截图:

20191119204122472.png

       这时map和flatMap的用法就清楚,如果某对象实例的属性本身就为Optional包装过的类型,那么就要使用flatMap方法,就像School::getTearch返回的就是Optional<Tearch>类型的,所以不用再使用Optional进行包装,这时就要选用flatMap方法,对于返回值是其他类型,需要Optional进行包装,如Student::getName得到是String类型的,就需要使用map方法在其外面包装一层Optional对象。

注意:Option类是判断空指针异常时用的,对于其他的if..else只要不是判断空指针的问题,就不要使用Option类,lz就陷入了一个误区,以为学了Option类,看见if...else就往Option来想,这么做真是大作特错。用Option的情况时在判断值是否为null时,Option就发挥作用了。

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

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

相关文章

flink window 框架详细解读

1. dataStream window group window time window 基于时间驱动 滚动时间窗口 tumble time window 滑动时间窗口 sliding time window 会话时间窗口 session time window count window 基于数据驱动 滚动计数窗口 tumble count window 滑动计数窗口 sliding count window 2. s…

皕杰报表之雷达图

雷达图&#xff08;又可称为戴布拉图、螂蛛网图&#xff09;是数据分析报表的一种。即将各项数据分析所得的数字或比率&#xff0c;就其比较重要的项目集中划在一个图表上&#xff0c;来表现一组数据的各项数据比率的情况&#xff0c;使用者能一目了然的了解这个数据的指标的变…

LeetCode中等题之通过最少操作次数使数组的和相等

题目 给你两个长度可能不等的整数数组 nums1 和 nums2 。两个数组中的所有值都在 1 到 6 之间&#xff08;包含 1 和 6&#xff09;。 每次操作中&#xff0c;你可以选择 任意 数组中的任意一个整数&#xff0c;将它变成 1 到 6 之间 任意 的值&#xff08;包含 1 和 6&#…

计算机组成大题分析(三)

假定计算机 M 字长为 16 位&#xff0c;按字节编址&#xff0c;连接 CPU 和主存的系统总线中地址线为 20 位、数据线为 8位&#xff0c;采用 16 位定长指今字&#xff0c;指令格式及其说明如下: 其中&#xff0c;op1-op3 为操作码&#xff0c;rs&#xff0c;t 和 rd 为通用寄存…

【计算机视觉+CNN】keras+ResNet残差网络实现图像识别分类实战(附源码和数据集 超详细)

需要源码和数据集请点赞关注收藏后评论区留言私信~~~ 一、深度卷积神经网络模型结构 1&#xff1a;LeNet-5 LeNet-5卷积神经网络首先将输入图像进行了两次卷积与池化操作&#xff0c;然后是两次全连接层操作&#xff0c;最后使用Softmax分类器作为多分类输出&#xff0c;它对…

MySQL插入汉字报错的解决方案

MySQL插入汉字报错的原因是字符集的问题&#xff0c;MySQL默认使用的是Latin&#xff08;拉丁文&#xff09;字符集&#xff0c;可以在创建数据库时指定其字符集&#xff1a;CREATE DATABASE test DEFAULT CHARACTER SET utf8 或者修改MySQL的配置文件&#xff0c;可以参考以下…

Qt事件循环嵌套,BlockingQueuedConnection与QWaitCondition比较

前言&#xff1a; 之前写过有关事件循环和条件变量的博客&#xff1a; Qt使用事件循环&#xff0c;信号&#xff0c;stop变量&#xff0c;sleep阻塞&#xff0c;QWaitConditionQMutex条件变量&#xff0c;退出子线程工作_大橘的博客-CSDN博客_qt stop函数 Qt事件循环&#x…

Unity3D导出Android工程中使用并交互

, 目录 1&#xff0c;版本信息 2&#xff0c;前期准备 Unity方面&#xff1a; Android方面&#xff1a; 3&#xff0c;Android与Unity3D交互 1&#xff0c;版本信息 unity2020 android studio 2021 *不要用android studio 2020系列&#xff0c;存在不能导入Library的b…

Spring学习 | Bean作用域生命周期

文章目录一、作用域1.1 xml文件中配置1.2 注解配置二、生命周期2.1 四个阶段2.2 添加后置处理器2.3 实现aware类型接口2.4 Bean 初始化的方式2.5 Bean 销毁的方式2.6 测试程序学习视频&#x1f3a5;&#xff1a;https://www.bilibili.com/video/BV1Vf4y127N5 一、作用域 ❓ 引入…

Linux系统中裸机按键中断的驱动方法

大家好&#xff0c;今天主要和大家聊一聊&#xff0c;如何实现按键中断的驱动​方法。 目录 ​第一&#xff1a;外部中断头文件实现 ​第二&#xff1a;外部中断源文件的具体实现 ​第三&#xff1a;编写对应的main.c函数 ​第一&#xff1a;外部中断头文件实现 #ifndef _…

基于keras平台CNN神经网络模型的服装识别分析

在许多介绍图像识别任务的介绍中&#xff0c;通常使用着名的MNIST数据集。 最近我们被客户要求撰写关于图像识别的研究报告&#xff0c;包括一些图形和统计输出。但是&#xff0c;这些数据存在一些问题&#xff1a; 1.太简单了。例如&#xff0c;一个简单的MLP模型可以达到99…

Java搭建宝塔部署实战毕设项目springboot客户管理系统源码

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 本期给大家带来一套Java开发的毕业设计项目&#xff0c;springboot客户管理系统源码&#xff0c;感兴趣的朋友可以自行下载搭建测试。 技术架构 技术框架&#xff1a;SpringBoot MySQL5.7 mybatis shiro Lay…

DDR4时序标准规范(一)

DDR4时序标准规范引脚描述DDR4 SDRAM寻址DDR4架构的模块描述功能描述简化状态机基本功能复位和初始化程序上电和初始化顺序电压稳定后的复位初始化顺序无控制的下电顺序引脚描述 标志类型功能CK_t, CK_c输入Clock: CK_t和CK_c是差分时钟输入。所有的地址和控制输入信号在CK_t的…

主成分分析(PCA)原理及R语言实现及分析实例

主成分分析&#xff08;PCA&#xff09;是一种数据降维技巧&#xff0c;它能将大量相关变量转化为一组很少的不相关变量&#xff0c;这些无关变量称为主成分。最近我们被客户要求撰写关于主成分分析&#xff08;PCA&#xff09;的研究报告&#xff0c;包括一些图形和统计输出。…

Vagrant搭建Centos

1.下载安装vagrant 01访问Vagrant官网 ​ https://www.vagrantup.com/ 02 点击Download ​ Windows&#xff0c;MacOS&#xff0c;Linux等 03 选择对应的版本 04 傻瓜式安装 05 命令行输入vagrant&#xff0c;测试是否安装成功,显示如下&#xff1a; 2.下载安装virtua…

半解析快速傅里叶变换

我们提出了一种处理傅里叶变换的方法&#xff0c;其并不需要二次多项式相位项的抽样&#xff0c;而是用解析的方法处理。我们提出该理论的同时也给出了几个例子证明其潜力。 1.简介 物理光学建模需要频繁地从空间转换到角频域&#xff0c;反之亦然。这可以由电场和磁场分…

网络编程(用于不同电脑之间的信息交互):UDP、TCP

网络编程&#xff1a; 在网络通信写一下&#xff0c;不同计算机运行的程序&#xff0c;可以进行数据传输 IP地址&#xff1a;设备&#xff08;手机、电脑等&#xff09;在网络中的地址&#xff0c;是唯一的标识 端口&#xff1a;应用程序在设备中唯一的表示 协议&#xff1a…

数据结构学习——表、查找

定义 设记录表L(R1 R2…其中Ri(L<i<n)为记录&#xff0c; 对给定的某个值k&#xff0c; 在表L中确定key k的记录的过程&#xff0c;称为查找。若表Lz中存在记录Ri de key k,记为Ri.key,则查找成功&#xff0c;返回该记录在表L中的序号i&#xff08;或Ri的地址&#xff…

优秀的 Verilog/FPGA开源项目介绍(二十)- 张量处理单元(TPU)

介绍张量处理单元( Tensor Processing Unit, TPU ) 是谷歌专门为神经网络机器学习开发的人工智能加速器 专用集成电路(ASIC) &#xff0c;特别是使用谷歌自己的TensorFlow软件。谷歌于 2015 年开始在内部使用 TPU&#xff0c;并于 2018 年将它们作为其云基础设施的一部分并通过…

Zabbix6.0使用教程 (三)—zabbix6.0的安装要求

接上篇&#xff0c;我们继续为大家详细介绍zabbix6.0的使用教程之zabbix6.0的安装部署。接下来我们将从zabbix部署要求到四种不同的安装方式逐一详细的为大家介绍。本篇讲的是部署zabbix6.0的要求。 zabbix6.0安装要求 硬件&#xff1a;内存和磁盘 Zabbix6.0安装 运行需要物…