image.png

image.png

1、创建 Optional 对象

1)Optional.empty
创建一个空的Optional对象:
Optional optCar = Optional.empty();
2)Optional.of
依据一个非空值创建一个Optional对象:
Optional optCar = Optional.of(car);
如果car是一个null,这段代码会立即抛出一个NullPointerException,而不是等到试图访问car的属性值时才返回错误。
3)Optional.ofNullable
可以创建一个允许null值的Optional对象:
Optional optCar = Optional.ofNullable(car);
如果car是null,那么得到的Optional对象就是个空对象。

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

从对象中提取信息是一种比较常见的模式。
比如,你可能想要从insurance公司对象中提取公司的名称。
提取名称之前,你需要检查insurance对象是否为null,代码如下所示:
image.png
为了支持这种模式,Optional提供了一个map方法。
它的工作方式如下:
image.png
map操作会将提供的函数应用于流的每个元素。
如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。
如 果Optional为空,就什么也不做。

3、使用 flatMap 链接 Optional 对象

如何重构如下的代码使其可以用安全的方式链接了多个方法。
image.png
如果使用map:
image.png
但这段代码无法通过编译。
optPerson是Optional类型的 变量, 调用map方法没有问题。
但getCar返回的是一个Optional类型的对象,这意味着map操作的结果是一个Optional>类型的对象。
因此,它对getInsurance的调用是非法的,因为最外层的optional对象包含了另一个optional对象的值,而它当然不会支持getInsurance方法。
flatMap方法会用流的内容替换每个新生成的流。换句话说,由方法生成的各个流会被合并或者扁平化为一个单一的流。
image.png
返回的Optional可能是两种情况:如果调用链上的任何一个方法返回一个空的Optional,那么结果就为空,否则返回的值就是你期望的保险公司的名称。
如上代码使用了orElse的方法,当Optional的值为空时,它会为其设定一个默认值。

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

采用orElse方法读取这个变量的值,使用这种方式还可以定义一个默认值,遭遇空的Optional变量时,默认值会作为该方法的调用返回值。
Optional类提供了多种方法读取 Optional实例中的变量值。
1)get()
是这些方法中最简单但又最不安全的方法。
如果变量存在,它直接返回封装的变量值,否则就抛出一个NoSuchElementException异常。
此外,这种方式即便相对于嵌套式的null检查,也并未体现出多大的改进。
2)orElse(T other)
它允许你在Optional对象不包含值时提供一个默认值。
3)orElseGet(Supplier<? extends T> other)
是orElse方法的延迟调用版,Supplier 方法只有在Optional对象不含值时才执行调用。
如果创建默认值是件耗时费力的工作, 应该考虑采用这种方式(借此提升程序的性能)。
或者你需要非常确定某个方法仅在 Optional为空时才进行调用,也可以考虑该方式(这种情况有严格的限制条件)。
4)orElseThrow(Supplier<? extends X> exceptionSupplier)
和get方法非常类似, 它们遭遇Optional对象为空时都会抛出一个异常,但是使用orElseThrow你可以定制希望抛出的异常类型。
5)ifPresent(Consumer<? super T>)
让你能在变量值存在时执行一个作为参数传入的方法,否则就不进行任何操作。

5、两个 Optional 对象的组合

假设有这样一个方法,它接受一个Person和一个Car对象,并以此为条件找到最便宜的保险公司:
public Insurance findCheapestInsurance(Person person, Car car) {
// 不同的保险公司提供的查询服务
// 对比所有数据
return cheapestCompany;
}
该方法可能会写出的一个null-安全的版本,它接受两个Optional对象作为参数, 返回值是一个Optional对象,如果传入的任何一个参数值为空,它的返回值亦为
空。
public Optional nullSafeFindCheapestInsurance(
Optional person, Optional car) {
if (person.isPresent() && car.isPresent()) {
return Optional.of(findCheapestInsurance(person.get(), car.get()));
} else {
return Optional.empty();
}
}
但这个方法的具体实现和之前曾经实现的null检查太相似了,方法接受一个Person和一个Car对象作为参数, 而二者都有可能为null。
所以可以更进一步的去优化。
public Optional nullSafeFindCheapestInsurance(
Optional person, Optional car) {
return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
}
这段代码中,对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它的Lambda表达式不会执行,这次调用会直接返回一个空的Optional对象。
反之,如果person对象存在,这次调用就会将其作为函数Function的输入,并按照与flatMap方法的约定返回一个Optional对象。
这个函数的函数体会对第二个Optional对象执行map操作,如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个nullSafeFindCheapestInsuranc方法的返回值也是一个空的Optional对象。
最后,如果person和car对象都存在,作为参数传递给map方法的Lambda表达式能够使用这两个值安全地调用原始的findCheapestInsurance方法,完成期望的操作。

6、使用 filter 剔除特定的值

在应用中经常需要调用某个对象的方法,查看它的某些属性。
比如,可能需要检查保险公司的名称是否为“Cambridge-Insurance”。
为了以一种安全的方式进行这些操作,首先需要确定引用指向的Insurance对象是否为null,之后再调用它的getName方法,如下所示:
image.png
使用Optional可以将改代码进行重构:
image.png
filter方法接受一个谓词作为参数。
如果Optional对象的值存在,并且它符合谓词的条件,filter方法就返回其值;否则它就返回一个空的Optional对象。
image.png

7、异常与 Optional 的对比

由于某种原因,函数无法返回某个值,这时除了返回null,比较常见的替代做法是抛出一个异常。
这种情况比较典型的例子是使用静态方法Integer.parseInt(String),将String转换为int。
在这个例子中,如果String无法解析到对应的整型,该方法就抛出一个NumberFormatException。
唯一的不同是,需要使用try/catch 语句,而不是使用if条件判断来控制一个变量的值是否非空。
可以编写一个工具方法,将这部分逻辑封装于其中,最终返回一个我们希望的Optional对象,代码如下所示。
image.png
所以可以将多个类似的方法封装到一个工具类中,称之为OptionalUtility。
通过这种方式,就能直接调用OptionalUtility.stringToInt方法。

8、避免使用基础类型的Optional对象

Optional也提供了基础类型——OptionalInt、OptionalLong以及OptionalDouble。
不推荐使用基础类型的Optional,因为基础类型的Optional不支持map、flatMap以及filter方法。
此外,Optional对象无法由基础类型的Optional组合使用。
比如,如果代码返回的是OptionalInt类型的对象,那么就不能将其作为方法引用传递给另一个Optional对象的flatMap方法。

思考:优化代码
image.png
image.png