使用Optional取代null

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qmqm011/article/details/82979299

本文参考书籍《Java 8实战》,陆明刚、劳佳  译,如有侵权,请联系删除!


如何为缺失的值建模

假设你需要处理下面这样的嵌套对象,这是一个拥有汽车及汽车保险的客户。

    public class Person {
        private Car car;
        public Car getCar() { return car; }
    }

    public class Car {
        private Insurance insurance;
        public Insurance getInsurance() { return insurance; }
    }
    
    public class Insurance {
        private String name;
        public String getName() { return name; }
    }

那么,下面这段代码存在怎样的问题呢?

    public String getCarInsuranceName(Person person) {
        return person.getCar().getInsurance().getName();
    }

很明显,如果一个人没有车(person.getCar()返回null),或者他的车没有保险(person.getCar().getInsurance()返回null),这段代码将会抛出NullPointerException。怎样做才能避免这种不期而至的NullPointerException呢?通常,可以在需要的地方添加null的检查,下面这个例子是我们试图在方法中避免NullPointerException的第一次尝试。

    public String getCarInsuranceName(Person person) {
        if (person != null) {
            Car car = person.getCar();
            if (car != null) {
                Insurance insurance = car.getInsurance();
                if (insurance != null) {
                    return insurance.getName();
                }
            }
        }
        return "Unknown";
    }

这种方式不具备扩展性,同时还牺牲了代码的可读性。下面的代码清单中,我们试图通过一种不同的方式避免这种问题。

    public String getCarInsuranceName(Person person) {
        if (person == null) {
            return "Unknown";
        }
        Car car = person.getCar();
        if (car == null) {
            return "Unknown";
        }
        Insurance insurance = car.getInsurance();
        if (insurance == null) {
            return "Unknown";
        }
        return insurance.getName();
    }

这种方案远非理想,现在这个方法有了四个截然不同的退出点,使得代码的维护异常艰难。我们需要更优雅的方式来对缺失的变量值建模。

Optional 类入门

Java 8中引入了一个新的类java.util.Optional<T>,这是一个封装Optional值的类。变量存在时, Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。你可能还有疑惑,null引用和Optional.empty()有什么本质的区别吗?从语义上,你可以把它们当作一回事儿,但是实际中它们之间的差别非常大:如 果 你 尝 试 解 引 用 一 个 null , 一 定 会 触 发 NullPointerException , 不 过 使 用Optional.empty()就完全没事儿,它是Optional类的一个有效对象,多种场景都能调用。使用Optional类意味着,如果你知道一个人可能有也可能没有车,那么Person类内部的car变量就不应该声明为Car,而是应该将其声明为Optional<Car>类型。如下:

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

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

    public class Insurance {
        private String name;
        public String getName() {
            return name;
        }
    }

代码中person引用的是Optional<Car>,而car引用的是Optional<Insurance>,这种方式非常清晰地表达了你的模型中一个person可能拥有也可能没有car的情形,同样, car可能进行了保险,也可能没有保险。注意,insurance公司的名称被声明成String类型,而不是Optional-<String>,这非常清楚地表明声明为insurance公司的类型必须提供公司名称。使用这种方式,一旦解引用insurance公司名称时发生NullPointerException,你就能非常确定地知道出错的原因,不再需要为其添加null的检查,因为null的检查只会掩盖问题,并未真正地修复问题。insurance公司必须有个名字,所以,如果你遇到一个公司没有名称,你需要调查你的数据出了什么问题,而不应该再添加一段代码,将这个问题隐藏。

创建 Optional 对象

1、声明一个空的Optional

可以通过静态工厂方法Optional.empty,创建一个空的Optional对象:

    Optional<Car> optCar = Optional.empty();

2、依据一个非空值创建Optional

可以使用静态工厂方法Optional.of,依据一个非空值创建一个Optional对象:

    Optional<Car> optCar = Optional.of(car);

如果car为null,这段代码会立即抛出一个NullPointerException,而不是等到你试图访问car的属性值时才返回一个错误。

3. 可接受null的Optional

使用静态工厂方法Optional.ofNullable,你可以创建一个允许null值的Optional对象:

    Optional<Car> optCar = Optional.ofNullable(car);

如果car为null,那么得到的Optional对象就是个空对象。

使用Optional对象

Optional类提供了多种方法读取Optional实例中的变量值。

1、get()

get()是这些方法中最简单但又最不安全的方法。如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。

2、orElse(T other)

如果Optional对象不包含值,那么将返回一个默认值。

3、orElseGet(Supplier<? extends T> other)

如果Optional对象不包含值,那么将使用Supplier获取一个对象。

4、orElseThrow(Supplier<? extends X> exceptionSupplier)

和get方法非常类似,如果Optional对象不包含值都会抛出一个异常,但是使用orElseThrow可以定制希望抛出的异常类型。

5、ifPresent(Consumer<? super T>)

ifPresent(Consumer<? super T>)能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

由于Optional类设计时就没特别考虑将其作为类的字段使用,所以它也并未实现Serializable接口。由于这个原因,如果你的应用使用了某些要求序列化的库或者框架,在域模型中使用Optional,有可能引发应用程序故障。

猜你喜欢

转载自blog.csdn.net/qmqm011/article/details/82979299