《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

  1. 通过静态工厂方法Optional.empty,创建一个空的Optional对象:
Optional<Car> optCar = Optional.empty();
  1. 使用静态工厂方法Optional.of,依据一个非空值(为空会抛异常)创建Optional对象:
Optional<Car> optCar = Optional.of(car);
  1. 使用静态工厂方法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 对象

flatMapmap的区别就在于它会判断,如果处理的结果已经是一个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 类型返回。
flatMapmap。只是如果返回值已经是 Optional 则不再用 Optional封装。
get如果该值存在,将该Optional 封装返回,否则抛NoSuchElementException
ifPresent如果值存在时执行作为参数传进来的lambda(消费者),否则就不进行任何操作。
isPresent如果值存在就返回 true,否则返回 false
of将指定Optional 封装之后返回,如果该值为 null,则抛出一个 NullPointerException异常。
ofNullable将指定Optional 封装之后返回,如果该值为 null,则返回一个空Optional对象。
orElse就直接返回,否则返回我们指定的 other
orElseGet就直接返回,否则执行参数给定的 lambda(生产者)生成我们想要的返回值 。
这里的目的是借助lambda特性,达到延迟执行效果。
orElseThrowget() ,只是可以抛自定义异常。参数是个生产者用来生成自定义异常。

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举例说明这种情景。实际上Java8MapgetOrDefault

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 小结

这一章中,你学到了以下的内容。

  1. null引用在历史上被引入到程序设计语言中,目的是为了表示变量值的缺失。(业务上此值可以为空)
  2. Java 8中引入了一个新的类java.util.Optional<T>对存在或缺失的变量值进行建模
  3. 你可以使用静态工厂方法Optional.empty、Optional.of以及Optional.ofNullable创建Optional对象。
  4. Optional类支持多种方法,比如map、flatMap、filter它们在概念上与Stream类中对应的方法十分相似
  5. 使用Optional会迫使你更积极地引用Optional对象,以应对变量值缺失的问题,最终,你能更有效地防止代码中出现不期而至的空指针异常。 (病毒传播,慎重)
  6. 使用Optional能帮助你设计更好的API,用户只需要阅读方法签名,就能了解该方法是否接受一个Optional类型的值。 (太重,慎重)

参考资料

java.util.Optional<T>

以上是关于《Java8实战》读书笔记09:用 Optional 处理值为 null 的情况的主要内容,如果未能解决你的问题,请参考以下文章

《Java8实战》读书笔记10:组合式异步编程 CompletableFuture

《Java8实战》读书笔记10:组合式异步编程 CompletableFuture

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

《Java8实战》读书笔记07:Lambda 重构测试和调试(设计模式实现)

《Java8实战》读书笔记13:Java8 与 Scala

《Java8实战》读书笔记13:Java8 与 Scala