用Optional取代null
2018-06-02 本文已影响94人
小鱼嘻嘻
null引发的问题以及为什么要避免null引用
先看一个例子
public static void main(String[] args) {
new Person().getCar().getInsurance().getName();
}
static class Person {
private Car car;
public Car getCar() {
return car;
}
public void setCar(Car car) {
this.car = car;
}
}
static class Car {
private Insurance insurance;
public Insurance getInsurance() {
return insurance;
}
public void setInsurance(Insurance insurance) {
this.insurance = insurance;
}
}
static class Insurance {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
意思也就是说获取一个人的车的保险公司的名称。可以看到的是,并不是每一个都有车,也并不是每一个车都有保险。这样的获取是会发生NullpointException,如何避免呢?Java8之前我们可能采用防御式编程。
Person p = new Person();
if (p != null) {
Car car = p.getCar();
if (car != null) {
Insurance insurance = car.getInsurance();
if (insurance != null) {
System.out.println(insurance.getName());
}
}
}
这样的代码会看着结构不清晰,可读性很差。如何解决呢?请看Java8引入的Optional。
Optional 类
- 入门
Optional<T> T就是需要处理的对象,当对象为空的时候返回的是一个空Optional对象,当你使用Optional.empty()是不会报空指针的。
重构之前的数据模型
static class Person {
private Optional<Car> car;
public Optional<Car> getCar() {
return car;
}
public void setCar(Optional<Car> car) {
this.car = car;
}
}
static class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() {
return insurance;
}
public void setInsurance(Optional<Insurance> insurance) {
this.insurance = insurance;
}
}
static class Insurance {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
- 应用Optional的几种模式
申明一个空的Optional:Optional.empty();
依据一个非空值创建Optional:Optional.of();
可接受null的Optional:Optional.ofNullable(); - 使用map从Optional中提取和转换值
Optional<Insurance> insurance = Optional.of(new Insurance());
Optional<String> s = insurance.map(Insurance::getName);
这种map的转换和之前的stream基本一样,只是这个是单个元素的转换而已。
- 使用flatMap链接Optional对象
String s1 = person.
flatMap(Person::getCar).
flatMap(Car::getInsurance).
map(Insurance::getName).
orElse("unknown");
对比我们的第一份代码这个代码结构就太清晰了,层次和逻辑也很清新。
默认行为以及解引用Optional对象
- get() 是这些方法中最简单又最不安全的方法,如果不存在的话是会抛异常的,这个方法慎用。
- orElse(T other) 这个的意思是允许你换一个其他的值,当返回的对象不存在的时候。
- orElseGet(Supplier<? extends T> other) 是orElse(T other)的延迟调用版,supplier方法只有在Optional不含值的时候才调用。
- orElseThrow(Supplier<? extends X> exceptionSupplier) 和get类型遇到Optional为空的时候回抛异常。
- ifPresent(Consumer<? super T>) 让你能在变量值存在时执行一个作为参数传入的方法。
两个Optional对象组合
假设你试图获取保险最便宜的。
private static Optional<Insurance> getCheapestInsurance(Optional<Person> person, Optional<Car> car) {
if (person.isPresent() && car.isPresent()) {
return getCheapestInsurance(person.get(), car.get());
}
return Optional.empty();
}
private static Optional<Insurance> getCheapestInsurance(Person person, Car Car) {
return null;
}
这种并不是最优的解决方法,请看下面:
return person.flatMap(p -> car.map(c -> getCheapestInsurance(p, c))).orElse(Optional.empty());
使用filter剔除特定的值
insurance.filter(insurance1 -> insurance1.getName().equals("AAA"))
.ifPresent(x -> System.out.println(x));
这个filter和stream的filter也是一样的。
方法 | 描述 |
---|---|
empty | 返回一个空的Optional实例 |
filter | 如果值存在并且满足提供的谓词,就返回Optional对象,否则返回空 |
flatMap | 如果值存在,就对该值执行mapping函数调用,返回一个Optional对象,否则就返回一个空Optional |
get | 如果值存在就返回Optional对象,否则报异常 |
ifPresent | 如果值存在就执行该方法,不存在什么也不做 |
isPresent | 存在就返回true,不存在就返回false |
map | 如果值存在就执行mapping函数调用 |
of | 将制定值用Optional封装之后返回,如果为空就返回一个Optional空对象 |
orElse | 如果有值就返回值,没有就返回一个默认值 |
orElseGet | 如果有值就返回,没有值就执行supplier函数 |
orElseThrow | 如果有值就返回,否则抛出指定的异常 |