首页 > 编程语言 >Java8的Optional简介

Java8的Optional简介

时间:2024-09-19 21:51:27浏览次数:10  
标签:map 简介 Insurance getCar Person Car Optional Java8

文章目录

注:本文主要参考了《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/

标签:map,简介,Insurance,getCar,Person,Car,Optional,Java8
From: https://blog.csdn.net/duke_ding2/article/details/142310643

相关文章

  • centos789手动无脑用sh脚本安装Java8
    #老师给的文件是jdk1.8版本,所以我这边写的也是8的脚本输入命令:mkdir–p/export/data#放置相关的数据文件输入命令mkdir–p/export/servers#软件的安装目录输入命令:mkdir–p/export/software上传文件jdk-8u241-linux-x64.tar.gz到/export/software目录然后写一个安装......
  • centos789手动无脑用sh脚本安装Java8
    #老师给的文件是jdk1.8版本,所以我这边写的也是1.8的脚本输入命令:mkdir–p/export/data#放置相关的数据文件输入命令mkdir–p/export/servers#软件的安装目录输入命令:mkdir–p/export/software上传文件jdk-8u241-linux-x64.tar.gz到/export/software目录然后写一个安......
  • k8s 中的 Ingress 简介【k8s 系列之三】
    〇、前言前边已经介绍了k8s中的相关概念和Service,本文继续看下什么是Ingress。Ingress的重要性不言而喻,它不仅统一了集群对外访问的入口,还提供了高级路由、七层负载均衡、SSL终止等关键功能,同时支持动态配置更新、灰度发布等高级特性。下文将进行详细介绍。一、关于Ingr......
  • 机器人领域的国际会议简介 机器人学术会议
    JCR分区是什么意思?JCR分区,全称为JournalCitationReports分区,是由科睿唯安(Clarivate)发布的期刊引证报告。它使用期刊的影响因子(IF)来评价期刊的影响力,并将期刊分为不同的区域。JCR分区包括254个学科小类,每个小类中的期刊根据影响因子高低被平均分为四个区:Q1、Q2、Q3和Q4。其......
  • 网络变压器简介、功能、分类、应用组成等综合概述-沃虎电子
    网络变压器:网络变压器是在有限局域网中各级网络设备中都具备的变压器元件。网络变压器(EthernetTransformer,也称数据汞模块,是网卡电路中不可或缺的部分,它主要包含中间抽头电容、变压器、自耦变压器、共模电感。其又叫网络隔离变压器、以太网变压器、网络滤波器、网口变压......
  • 硬件描述语言简介
    VerilogHDL(Verilog)Verilog的历史1983年,GatewayDesignAutomation公司推出了Verilog语言,开发了仿真与验证工具。1983年,verilog的仿真器-Verilog-XL被推出,仿真速度快具有交互式调试手段。1987年,synopsys公司的综合软件开始接受Verilog输入1989年,Cadence公司收购了GDA......
  • 【Python系列】JSON和JSONL简介
    ......
  • LLM应用实战: 文档问答系统Kotaemon-1. 简介及部署实践
    1.背景本qiang~这两周关注到一个很火的开源文档问答系统Kotaemon,从8月28日至今短短两周时间,github星标迅猛增长10K,因此计划深挖一下其中的原理及奥秘。本篇主要是Kotaemon的简介信息,涉及到主要特点,与传统文档RAG的区别,如何部署、以及效果演示。后续内容会针对核心模块进行拆解......
  • JDBC简介与应用:Java数据库连接的核心概念和技术
    简短介绍JDBC及其重要性。简短介绍JDBCJDBC(JavaDatabaseConnectivity)是一种用于执行SQL语句的JavaAPI并且独立于特定的数据库厂商。它允许开发者以一种标准的方式从Java应用程序中访问关系型数据库,这意味着一旦你掌握了JDBC的基本操作,你可以轻松地将你的应用程......
  • Vue.js入门系列(三十一):Element-UI的基本使用与按需引入、Vue 3简介及使用 Vue CLI 与 V
    个人名片......