《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况
Posted 笑虾
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况相关的知识,希望对你有一定的参考价值。
《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况
大名顶顶的NullPointerException
从我们入坑起就阴魂不散。我也增想过每次都要 if(obj != null)
代码太难看,但是很多时候不判又不严谨,是不是要封装一个工具类来干这事。很好 java.util.Optional<T> 就是那个工具类。(可恨他们又趁我没想好,就剽窃走了。)
Optional
很简单,就几个方法,看明白用法就行了。书中建议如果一个值
在业务层面允许为null
就应该用Optional
包裹它。这个需要慎重,起码你的团队都听你的才行,否则…
第10章用Optional 取代 null
本章内容
null引用引发的问题,以及为什么要避免null引用
从null到Optional:以null安全的方式重写你的域模型
让Optional发光发热: 去除代码中对null的检查
读取Optional中可能值的几种方法
对可能缺失值的再思考
10.2 Optional 类入门
Optional
的思路就是把实际的值
(对象)保护起来,对外开放一组接口,用于满足当 值 == null
或 值 != null
时进行某些操作。
10.3 应用 Optional 的几种模式
10.3.1 创建 Optional 对象
构造函数都是 private 的不能 new 。
- 通过静态工厂方法
Optional.empty
,创建一个空的Optional
对象:
Optional<Car> optCar = Optional.empty();
- 使用静态工厂方法
Optional.of
,依据一个非空值
(为空会抛异常)创建Optional
对象:
Optional<Car> optCar = Optional.of(car);
- 使用静态工厂方法
Optional.ofNullable
,你可以创建一个允许null
值的Optional
对象:
Optional<Car> optCar = Optional.ofNullable(car);
10.3.2 使用 map 从 Optional 对象中提取和转换值
从概念上,这与我们在第4章和第5章中看到的流的map
方法很像。
不同的是流
中通常会有多个
元素,而Optional
肚子里只可能有一个值
或null
。
map
:用给定的 lambda
处理 value
然后包裹在 Optional
中返回。比如:Optional<值>
10.3.3 使用 flatMap 链接 Optional 对象
flatMap
与map
的区别就在于它会判断,如果处理的结果已经是一个Optional
它就直接返回,否则同 map
。
在域模型中使用Optional,以及为什么它们无法序列化
____在代码清单10-4中,我们展示了如何在你的域模型中使用Optional,将允许缺失或者暂无定义的变量值用特殊的形式标记出来。然而,Optional类设计者的初衷并非如此,他们构思时怀揣的是另一个用例。这一点,Java语言的架构师Brian Goetz曾经非常明确地陈述过,Optional的设计初衷仅仅是要支持能返回Optional对象的语法。
____由于Optional类设计时就没特别考虑将其作为类的字段使用,所以它也并未实现Serializable接口。由于这个原因,如果你的应用使用了某些要求序列化的库或者框架,在域模型中使用Optional,有可能引发应用程序故障。然而,我们相信,通过前面的介绍,你已经看到用Optional声明域模型中的某些类型是个不错的主意,尤其是你需要遍历有可能全部或部分为空,或者可能不存在的对象时。如果你一定要实现序列化的域模型,作为替代方案,我们建议你像下面这个例子那样,提供一个能访问声明为Optional、变量值可能缺失的接口,代码清单如下:public class Person private Car car; public Optional<Car> getCarAsOptional() return Optional.ofNullable(car);
10.3.4 默认行为及解引用 Optional 对象
略。。。内容合并入 表10-1 Optional类的方法
10.3.5 两个 Optional 对象的组合
public Insurance findCheapestInsurance(Person person, Car car)
// 通过不同的保险公司提供的查询服务,获取数据
// 对比所有数据,得出保险最便宜的一家保险公司。
return cheapestCompany;
如下的组合实现了仅当Optional<Person>
和Optional<Car>
都有值时才会走到findCheapestInsurance(p, c)
这一步。(但是从代码上看,个人觉得这操作已经有点入魔了。我不希望Optional
侵入的太深。)
public Optional<Insurance> foo( Optional<Person> person, Optional<Car> car)
return person.flatMap(p -> car.map(c -> findCheapestInsurance(p, c)));
10.3.6 使用 filter 剔除特定的值
直接上代码,这里如果不满足年龄条件p.getAge() >= minAge
得到的就是一个空Optional
。
到了.orElse("Unknown");
就会得到Unknown
。
public String getCarInsuranceName(Optional<Person> person, int minAge)
return person.filter(p -> p.getAge() >= minAge)
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("Unknown");
表10-1 Optional类的方法
方法 | 介绍 |
---|---|
empty | 返回一个空的 Optional 实例。 |
filter | 如果值存在 且满足作为参数提供的lambda(谓词) ,就返回包含该值的 Optional 对象;否则返回一个空的 Optional 对象。 |
map | 如果值存在 ,就对该值执行参数给定的lambdaR apply(T t) ,结果包装为 Optional 类型返回。 |
flatMap | 同 map 。只是如果返回值已经是 Optional 则不再用 Optional 封装。 |
get | 如果该值存在,将该值 用 Optional 封装返回,否则抛NoSuchElementException 。 |
ifPresent | 如果值存在 时执行作为参数传进来的lambda(消费者) ,否则就不进行任何操作。 |
isPresent | 如果值存在 就返回 true ,否则返回 false 。 |
of | 将指定值 用 Optional 封装之后返回,如果该值为 null ,则抛出一个 NullPointerException 异常。 |
ofNullable | 将指定值 用 Optional 封装之后返回,如果该值为 null ,则返回一个空Optional 对象。 |
orElse | 有值 就直接返回,否则返回我们指定的 other 。 |
orElseGet | 有值 就直接返回,否则执行参数给定的 lambda(生产者) 生成我们想要的返回值 。这里的目的是借助lambda特性,达到延迟执行效果。 |
orElseThrow | 同 get() ,只是可以抛自定义异常。参数是个生产者 用来生成自定义异常。 |
10.4 使用 Optional 的实战示例
10.4.1 用 Optional 封装可能为 null 的值
每次你希望安全地将可能为null
的对象替换为Optional
对象时,都可以考虑使用这种方法。
HashMap<Object, Object> map = new HashMap<>();
Optional<Object> value = Optional.ofNullable(map.get("key"));
这里只是用Map
举例说明这种情景。实际上Java8
的Map
有getOrDefault
。
HashMap<String, String> map = new HashMap<>();
String str = map.getOrDefault("key","默认值");
System.out.println(str); // 默认值
10.4.2 异常与 Optional 的对比
代码清单10-6 将String
转换为Integer
,并返回一个Optional
对象。
public static Optional<Integer> stringToInt(String s)
try
return Optional.of(Integer.parseInt(s));
catch (NumberFormatException e)
return Optional.empty();
作者:应该避免使用基础类型的Optional对象:OptionalInt、OptionalLong、OptionalDouble
10.4.3 把所有内容整合起来
Insurance i = new Insurance("非常保险");
Car c = new Car(Optional.of(i));
Person p = new Person(Optional.of(c));
Optional<Person> optPerson = Optional.of(p);
String insuranceName = optPerson
// flatMap 如果返回值已经是 Optional 则直接返回,否则同 map。
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
// map 用给定的 lambda 处理 value 然后包裹在 Optional 中返回。
.map(Insurance::getName)
.orElse("无名保险");
System.out.println(insuranceName); // 非常保险
10.5 小结
这一章中,你学到了以下的内容。
null
引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。(业务上此值可以为空)Java 8
中引入了一个新的类java.util.Optional<T>
,对存在或缺失的变量值进行建模。- 你可以使用静态工厂方法
Optional.empty、Optional.of
以及Optional.ofNullable
创建Optional
对象。 Optional
类支持多种方法,比如map、flatMap、filter
,它们在概念上与Stream
类中对应的方法十分相似。使用(病毒传播,慎重)Optional
会迫使你更积极地引用Optional
对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。使用(太重,慎重)Optional
能帮助你设计更好的API
,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional
类型的值。
参考资料
以上是关于《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况的主要内容,如果未能解决你的问题,请参考以下文章
《Java8实战》读书笔记10:组合式异步编程 CompletableFuture
《Java8实战》读书笔记10:组合式异步编程 CompletableFuture
《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)