1.0 Optional 入门
引入了一个名为 java.util.Optional<T>的新的类。这种方式对可能缺失的值建模, 而不是直接将null赋值给变量所带来的好处。 反思的是:如何在你的域模型中使用optional值。去设计更好的API—— 用户只需要阅读方法签名就能知道它是否接受一个optional的值
- 使用新的类意味着,如果你知道一个人可能有也可能没有车,那么Person类内部的car变量就不应该声明为Car,遭遇某人没有车时把null引用赋值给它,而是应该像如下图那样直接将其声明为Optional类型。
- 变量存在时,
Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空” 的Optional对象,由方法Optional.empty()_(静态工厂方法)返回。 null引用和Optional.empty()本质的区别: 从语义上是相同,但是实际中它们之间的差别非常大:如果你尝试解引用一个null,一定会触发NullPointerExcekognption,不过使用Optional.empty()不会,它是Optional类的一个有效对象,多种场景都能调用,
比如在声明变量时使用的是
Optional<Car>类型,而不是Car类型,这句声明非常清楚地表明了这里发生变量缺失是允许的。与此相反,使用Car这样的类型,可能将变量赋值为null,这意味你只能依赖你对业务模型的理解,判断一个null是否属于该变量的防止NullPointerExcekognption;
2.0 使用Optional重新进行建模
@Data
public class Insurance {
private String name;
}
@Data
public class Car {
private Optional<Insurance> insurance;
}
@Data
public class Person {
private Optional<Car> car;
}
代码中
person引用的是Optional<Car>, car引用的是Optional<Insurance>,这样的的模型表达person可能拥有也可能没有car的情形,car可能进行了Insurance,也可能没有Insurance。insurance公司的名称被声明成String类型,而不是Optional- <String>,声明为insurance公司的类型必须提供公司名称。使用这种方式, 一旦解引用insurance公司名称时发生NullPointerException,能非常确定地知道出错的原因,不再需要为其添加null的检查,因为null的检查只会掩盖问题,并未真正地修复问题。 始终如一地使用Optional,能非常清晰地界定出变量值的缺失是结构上的问题,还是算法上的缺陷,抑或是数据中问题。另外,引入Optional类的意图并非要消除每一个null引用。是为更好地设计出普适的API, 让程序员看到方法签名,就能了解它是否接受一个Optional的值。
3.0 应用Optional的模式
3.1创建Optional对象
实例化optional对象的方法:
empty():静态工厂方法,创建一个空的Optional对象
Optional<String> emptyOpt = Optional.empty();
如果对emptyOpt变量调用isPresent()方法会返回false,调用get()方法抛出NullPointerException异常。
-
of()创建一个非空的Optional对象的静态工厂方法String str = "Hello"; Optional<String> notNullOpt = Optional.of(str) -
ofNullable()方法接收一个可以为null的值:Optional<String> nullableOpt = Optional.ofNullable(str);
3.2使用map从 Optional对象中提取和转换值
// 通常
String roleId = null;
if(user != null){
roleId = user.getRoleId();
}
// Optional
Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::g
map操作会将提供的函数应用于流的每个元素。把Optional对象看成一种特殊的集合数据,它至多包含一个元素。如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。如果Optional为空,就什么也不做。下图把一个将正方形 换为三角形的函数,分别传递给正方形和Optional正方形流的map方法之后的结果。
3.3 使用flatMap链接 Optional对象
编译无法通过 原因:optPerson是Optional类型的变量, 调用map方法应该没有问题。但getCar返回的是一个Optional类型的对象
(Optional<Optional<Car>> car = optPerson.map(Person::getCar); ),因此,它对getInsurance的调用是非法的,因为最外层的optional对象包含了另一个optional 对象的值,也就是出现了嵌套式optional 结构。
解决:就是让它能够接受一个函数(新流)作为参数
flatMap方法会将这种两层的Optional对象转换为包含三角形的单一Optional对象。所以就要先理解他们的区别。
3.3.4 Stream的Map与flatMap的区别
- 相同:输入都是stream的每一个元素
- 异点:
map的输出对应一个元素,必然是一个元素(null也是要返回)flatmap是0或者多个元素(为null的时候其实就是0个元素)。
在Optional的map和flatmap:
map是把结果自动封装成一个Optional,但是flatmap需要你自己去封装。
Optional内的Person也被转换成了一个两层的Optional对象,最终它们会被flagMap操作合并。简单地看成把两个Optional对象结合在一起,如果其中有一个对象为空,就构成一个空的Optional对象。
// 提供一个能访问声明为Optional、变量值可能缺失的接口
public Optional<Car> getCarAsOptional() {
return Optional.ofNullable(car);
}
public String getCarInsuranceName(Optional<Person> person){
String unKnown = person.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("unKnown");
return unKnown;
}
3.4 默认行为及解引用Optional对象
-
orElse():遇空的Optional变量时,如果有值就返回,否则返回一个给定的值作为默认值
String str = "hello"; Optional<String> strOpt = Optional.of(str); String orElseResult = strOpt.orElse("hello word"); -
orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值
String orElseGet = strElseGet(()->"hello") -
orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。
String orElseThrow = strOpt.orElseThrow( ()-> new IllegalArgumentException("Argument 'str' cannot be null or blank.") ) -
ifPresent(),该方法接收一个Consumer<? super T>函数式接口,一般用于将信息打印到控制台,如果值存在,就执行使用该值的方法调用,否则什么也不做Optional<String> strOpt = Optional.of("hellp"); strOpt.ifPresent(System.out::println) -
filer()可用于判断Optional对象是否满足给定条件,一般用于条件过滤:Optional<String> optional = Optional.of("newhzong@qq.com"); optional = optional.filter(str - >str.contains("164"));
如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值