《java 8 实战》读书笔记 -第十章 用Optional取代null

1、Optional 类入门

Java 8中引入了一个新的类java.util.Optional<T>。变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”
的Optional对象,由方法Optional.empty()返回。java

2、应用 Optional 的几种模式

1.建立 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对象:spa

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

若是car是null,那么获得的Optional对象就是个空对象。设计

Optional提供了一个 get方法用于获取Optional变量中的值,不过get方法在遭遇到空的Optional对象时也会抛出异常,因此不按照约定的方式使用它,又会让咱们再度陷入由null引发的代码维护的梦魇。

2.使用 map 从 Optional 对象中提取和转换值

好比,你可能想要从insurance公司对象中提取公司的名称。提取名称以前,你须要检查insurance对象是否为null,代码以下所示:code

String name = null; 
if(insurance != null){ 
 name = insurance.getName(); 
}

用Optional实现:对象

Optional<Insurance> optInsurance = Optional.ofNullable(insurance); 
Optional<String> name = optInsurance.map(Insurance::getName);

原理示意:
图片描述接口

3.使用 flatMap 连接 Optional 对象

Optional<Person> optPerson = Optional.of(person); 
Optional<String> name = 
 optPerson.map(Person::getCar) //编译没法经过
 .map(Car::getInsurance) 
 .map(Insurance::getName);

不幸的是,这段代码没法经过编译。为何呢?optPerson是Optional<Person>类型的变量, 调用map方法应该没有问题。但getCar返回的是一个Optional<Car>类型的对象,这意味着map操做的结果是一个Optional<Optional<Car>>类型的对象。所以,它对getInsurance的调用是非法的,由于最外层的optional对象包含了另外一个optional对象的值,而它固然不会支持getInsurance方法。
正确作法:图片

public String getCarInsuranceName(Optional<Person> person) { 
 return person.flatMap(Person::getCar) 
 .flatMap(Car::getInsurance) 
 .map(Insurance::getName) //Insurance::getName返回的是String类型,不是Optional
//返回的Optional多是两种状况:若是调用链上的任何一个
//方法返回一个空的Optional,那么结果就为空,不然返回的值就是你指望的保险公司的名称。
 .orElse("Unknown"); 
}

图片描述

在域模型中使用Optional,以及为何它们没法序列化
因为Optional类设计时就没特别考虑将其做为类的字段使用,因此它也并未实现Serializable接口。
若是你必定要实现序列化的域模型,做为替代方案,咱们建议你像下面这个例子那样,提供一个能访问声明为Optional、变量值可能缺失的接口,代码清单以下:

public class Person { 
 private Car car; 
 public Optional<Car> getCarAsOptional() { 
 return Optional.ofNullable(car); 
 } 
}

4. 默认行为及解引用 Optional 对象

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

  • get()是这些方法中最简单但又最不安全的方法。若是变量存在,它直接返回封装的变量值,不然就抛出一个NoSuchElementException异常。
  • orElse(T other)是咱们在代码清单10-5中使用的方法,正如以前提到的,它容许你在Optional对象不包含值时提供一个默认值。
  • orElseGet(Supplier<? extends T> other)是orElse方法的延迟调用版,Supplier方法只有在Optional对象不含值时才执行调用。若是建立默认值是件耗时费力的工做,你应该考虑采用这种方式(借此提高程序的性能),或者你须要很是肯定某个方法仅在Optional为空时才进行调用,也能够考虑该方式(这种状况有严格的限制条件)。
  • orElseThrow(Supplier<? extends X> exceptionSupplier)和get方法很是相似,它们遭遇Optional对象为空时都会抛出一个异常,可是使用orElseThrow你能够定制但愿抛出的异常类型。
  • ifPresent(Consumer<? super T>)让你能在变量值存在时执行一个做为参数传入的方法,不然就不进行任何操做

5.以不解包的方式组合两个Optional对象

public Insurance findCheapestInsurance(Person person, Car car) { 
 // 不一样的保险公司提供的查询服务
 // 对比全部数据
 return cheapestCompany; 
}
public Optional<Insurance> nullSafeFindCheapestInsurance( 
 Optional<Person> person, Optional<Car> car) { 
 return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c))); 
}

这段代码中,你对第一个Optional对象调用flatMap方法,若是它是个空值,传递给它的Lambda表达式不会执行,此次调用会直接返回一个空的Optional对象。反之,若是person对象存在,此次调用就会将其做为函数Function的输入,并按照与flatMap方法的约定返回一个Optional<Insurance>对象。这个函数的函数体会对第二个Optional对象执行map操做,若是第二个对象不包含car,函数Function就返回一个空的Optional对象,整个nullSafeFindCheapestInsuranc方法的返回值也是一个空的Optional对象。最后,若是person和car对象都存在,做为参数传递给map方法的Lambda表达式可以使用这两个值安全地调用原始的findCheapestInsurance方法,完成指望的操做。

6.使用 filter 剔除特定的值

Optional<Insurance> optInsurance = ...; 
optInsurance.filter(insurance -> 
 "CambridgeInsurance".equals(insurance.getName())) 
 .ifPresent(x -> System.out.println("ok"));

filter方法接受一个谓词做为参数。若是Optional对象的值存在,而且它符合谓词的条件,
filter方法就返回其值;不然它就返回一个空的Optional对象。
Optional类的方法:
图片描述
图片描述

3、使用 Optional 的实战示例

1.基础类型的Optional对象

与 Stream对象同样,Optional也提供了相似的基础类型——OptionalInt、OptionalLong以及OptionalDouble,若是Stream对象包含了大量元素,出于性能的考量,使用基础类型是不错的选择,但对Optional对象而言,这个理由就不成立了,由于Optional对象最多只包含一个值。咱们不推荐你们使用基础类型的Optional,由于基础类型的Optional不支持map、flatMap以及filter方法,而这些倒是Optional类最有用的方法。

相关文章
相关标签/搜索