Java8的Optional简介

注:本文主要参考了《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/
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值