当前位置: 首页 > news >正文

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.mrgr.cn/news/28672.html

相关文章:

  • nuxt3添加wowjs动效
  • 随手记:简单实现纯前端文件导出(XLSX)
  • 【MinIO】Python 运用 MinIO 实现简易文件系统
  • 蓝桥杯每日真题 - 第7天
  • odoo17 owl 前端 顶部导航栏右侧添加自定义按钮
  • 细说STM32单片机USART中断收发RTC实时时间并改善其鲁棒性的另一种方法
  • 计算机三级网络技术总结(三)
  • 在基于线程的环境中运行 MATLAB 函数
  • Mysql | 知识 | 幻读是如何解决的
  • 并查集的应用
  • Python3网络爬虫开发实战(15)Scrapy 框架的使用
  • 找搭子是什么意思?有没有找搭子的平台?靠谱找搭子软件推荐!
  • 7.4 溪降技术:滑行
  • 【机器学习】--- 自监督学习
  • 华硕产品资料的查询方法
  • 【Kubernetes】常见面试题汇总(二十一)
  • 如何避免长距离遗忘问题
  • 18、Python如何读写csv文件
  • 关于一道逻辑思维训练题的理解(手表、闹钟、标准时间的骗局)
  • 【计网面试真题】If-Modified-Since和Etag有什么区别
  • 简单的16位CPU(中央处理单元) verilog设计 (完整全部代码)
  • ST表(算法篇)
  • 音视频开发之旅(94)-多模态之Blip-2
  • 第一次安装Pytorch
  • MessagesPlaceholder
  • uniapp中实现<text>文本内容点击可复制或拨打电话