主题列表:juejin, github, smartblue, cyanosis, channing-cyan, fancy
贡献主题:
theme: juejin
highlight: juejin
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对象值