大名顶顶的NullPointerException从我们入坑起就阴魂不散。我也增想过每次都要 if(obj != null) 代码太难看,但是很多时候不判又不严谨,是不是要封装一个工具类来干这事。很好 java.util.Optional<T> 就是那个工具类。(可恨他们又趁我没想好,就剽窃走了。)
Optional很简单,就几个方法,看明白用法就行了。书中建议如果一个值在业务层面允许为null就应该用Optional包裹它。这个需要慎重,起码你的团队都听你的才行,否则…
本章内容
null引用引发的问题,以及为什么要避免null引用
从null到Optional:以null安全的方式重写你的域模型
让Optional发光发热: 去除代码中对null的检查
读取Optional中可能值的几种方法
对可能缺失值的再思考

Optional的思路就是把实际的值(对象)保护起来,对外开放一组接口,用于满足当 值 == null 或 值 != null 时进行某些操作。
构造函数都是 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);
从概念上,这与我们在第4章和第5章中看到的流的map方法很像。
不同的是流中通常会有多个元素,而Optional肚子里只可能有一个值或null。
map:用给定的 lambda 处理 value 然后包裹在 Optional 中返回。比如: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); } }
- 1
- 2
- 3
- 4
- 5
- 6
略。。。内容合并入 表10-1 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)));
}
直接上代码,这里如果不满足年龄条件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");
}
| 方法 | 介绍 |
|---|---|
| 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() ,只是可以抛自定义异常。参数是个生产者用来生成自定义异常。 |
每次你希望安全地将可能为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-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
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); // 非常保险
这一章中,你学到了以下的内容。
null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。(业务上此值可以为空)Java 8中引入了一个新的类java.util.Optional<T>,对存在或缺失的变量值进行建模。Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象。Optional类支持多种方法,比如map、flatMap、filter,它们在概念上与Stream类中对应的方法十分相似。Optional会迫使你更积极地引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。