场景
Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验:
Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验_霸道流氓气质的博客-CSDN博客
上面在讲Guava时讲过Optional,下面做具体的入门示例讲解。
构建一个关系
public class Insurance {
private String name;
public String getName() {
return name;
}
}
public class Car {
private Insurance insurance;
public Insurance getInsurance() {
return insurance;
}
}
public class Person {
private Car car;
public Car getCar() {
return car;
}
}
人-购买车-车买保险的关系类如上。
如果需要获取保险的名称可能需要做多重非空校验,如果person不为空,则调用person的getCar获取car对象,
如果car不为空,获取car的保险insurance,如果保险不为空,则获取保险名称。
注:
博客:
https://blog.csdn.net/badao_liumang_qizhi
实现
1、Java 8中引入了一个新的类java.util.Optional<T>。这是一个封装Optional值的类 。
变量存在时,Optional类只是对类简单封装。变量不存在时,缺失的值会被建模成一个“空”的Optional对象,
由方法Optional.empty()返回。Optional.empty()方法是一个静态工厂方法,它返回Optional类的特定单一实例。
//一 、创建Optional 对象
//1、声明一个空的Optional
Optional<Object> empty = Optional.empty();
//2、依据一个非空值创建Optional
Car car = new Car();
Optional<Object> o = Optional.of(car);
//3、可接受null的Optional
Optional<Object> o1 = Optional.ofNullable(null);
2、使用map从Optional对象中提取和转换值
把上面新建的三个类分别修改为
public class Insurance {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
import java.util.Optional;
public class Car {
private Optional<Insurance> insurance;
public Optional<Insurance> getInsurance() {
return insurance;
}
public void setInsurance(Optional<Insurance> insurance) {
this.insurance = insurance;
}
}
import java.util.Optional;
public class Person {
private Optional<Car> car;
public Optional<Car> getCar() {
return car;
}
public void setCar(Optional<Car> car) {
this.car = car;
}
}
这里不对保险进行修改,是因为在业务上认为保险一定是有名字,但是车不一定买保险,人不一定买车。
Optional提供了一个map方法
Insurance insurance = new Insurance();
insurance.setName("badao");
Optional<Insurance> insurance1 = Optional.ofNullable(insurance);
Optional<String> name = insurance1.map(Insurance::getName);
System.out.println(name.get());//badao
Optional<Insurance> insurance2 = Optional.ofNullable(null);
Optional<String> name2 = insurance2.map(Insurance::getName);
System.out.println(name2);//Optional.empty
这里的map与流的map方法相差无几。map操作会将提供的函数应用于流的每个元素。
可以把Optional对象看成一种特殊的集合数据,它至多包含一个元素。
如果Optional包含一个值,那函数就将该值作为参数传递给map,对该值进行转换。
如果Optional为空,就什么也不做。
3、使用flatMap链接Optional对象
如果是需要链接Optional对象时需要使用flatMap,比如
Car car1 = new Car();
car1.setInsurance(Optional.of(insurance));
Person person = new Person();
person.setCar(Optional.of(car1));
Optional<Person> person1 = Optional.of(person);
//处理潜在可能缺失的值时,使用Optional具有明显的优势。
// 这一次,你可以用非常容易却又普适的方法实现之前你期望的效果——不再需要使用那么多的条件分支,也不会增加代码的复杂性。
String s = person1
.flatMap(Person::getCar)
.flatMap(Car::getInsurance)
.map(Insurance::getName)
.orElse("默认名字");
System.out.println(s);
4、操纵由Optional对象构成的Stream
比如要找出person列表中所使用的保险公司名称(不含重复项)
构造一波模拟数据
Insurance insurance1 = new Insurance();
insurance1.setName("badao");
Car car1 = new Car();
car1.setInsurance(Optional.of(insurance1));
Person person1 = new Person();
person1.setCar(Optional.of(car1));
Insurance insurance2 = null;
Car car2 = new Car();
car2.setInsurance(Optional.ofNullable(insurance2));
Person person2 = new Person();
person2.setCar(Optional.of(car2));
Car car3 = null;
Person person3 = new Person();
person3.setCar(Optional.ofNullable(car3));
ArrayList<Person> personArrayList = new ArrayList<Person>() {
{
this.add(person1);
this.add(person2);
this.add(person3);
}
};
实现
Stream<Optional<String>> optionalStream = personArrayList.stream()
.map(Person::getCar)
.map(car -> car.flatMap(Car::getInsurance))
.map(insurance -> insurance.map(Insurance::getName));
上面三个操作得到了一个Stream<Optional<String>>对象,这些Optional对象中的一些可能为空,因为有的人可能并没有汽车,
或者有汽车但是没有投保。使用Optional,即便是碰到了值缺失的情况,你也不需要再为这些操作是否“空安全”(null-safe)
而烦心了。然而,你现在碰到了新的问题,怎样去除那些空的Optional对象,解包出其他对象的值,并把结果保存到集合Set中。
//去除空的对象
Set<String> collect = optionalStream.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toSet());
System.out.println(collect);
各阶段的类型明细为
5、两个Optional对象的组合
假设有这样一个方法,它接受一个Person和一个Car对象,并以此为条件对外部提供的服务进行查询,
通过一些复杂的业务逻辑,试图找到满足该组合的最便宜的保险公司。
public Insurance findCheapestInsurance(Person person,Car car){
return new Insurance();
}
需要接收两个Optional对象作为参数,返回值是一个Optional<Insurance>对象,如果传入的任何一个参数值为空,
它的返回值亦为空。
public Optional<Insurance> nullSafeFindInsurance1(Optional<Person> person,Optional<Car> car){
if(person.isPresent() && car.isPresent()){
return Optional.of(findCheapestInsurance(person.get(),car.get()));
}else{
return Optional.empty();
}
}
其实还可以用如下一行语句实现
public Optional<Insurance> nullSafeFindInsurance2(Optional<Person> person,Optional<Car> car){
return person.flatMap(person1 -> car.map(car1 -> findCheapestInsurance(person1,car1)));
}
对第一个Optional对象调用flatMap方法,如果它是个空值,传递给它的Lambda表达式就不会执行,
这次调用会直接返回一个空的Optional对象。反之,如果person对象存在,这次调用就会将其作为函数Function的输入,
并按照与flatMap方法的约定返回一个Optional<Insurance>对象。这个函数的函数体会对第二个Optional对象执行map操作,
如果第二个对象不包含car,函数Function就返回一个空的Optional对象,整个nullSafeFindCheapestInsurance方法的返回值
也是一个空的Optional对象。最后,如果person和car对象都存在,那么作为参数传递给map方法的Lambda表达式就能
够使用这两个值安全地调用原始的findCheapestInsurance方法。
6、使用filter剔除特定的值
经常需要调用某个对象的方法,查看它的某些属性。 比如,你可能需要检查保险公司的名称是否为“badao”。
为了以一种安全的方式进行这些操作,你首先需要确定引用指向的Insurance对象是否为null,之后再调用它的getName方法。
Insurance insurance1 = new Insurance();
insurance1.setName("badao");
if(insurance1 !=null && "badao".equals(insurance1.getName())){
System.out.println("ok");
}
可以将其使用filter重构
Optional<Insurance> insurance11 = Optional.of(insurance1);
insurance11.filter(insurance -> "badao".equals(insurance.getName()))
.ifPresent(x -> System.out.println("ok"));
7、Optional类的方法
方法 | 描述 |
empty | 返回一个空的Optional实例 |
filter | 如果值存在并且满足提供的谓词,就返回包含该值的Optional对象;否则返回一个空的Optional对象 |
flatMap | 如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象 |
get | 如果值存在,就将该值用Optional封装返回,否则抛出一个NoSuchElementException异常 |
ifPresent | 如果值存在,就执行使用该值的方法调用,否则什么也不做 |
ifPresentOrElse | 如果值存在,就以值作为输入执行对应的方法调用,否则执行另一个不需任何输入的方法 |
isPresent | 如果值存在就返回true,否则返回false |
map | 如果值存在,就对该值执行提供的mapping函数调用 |
of | 将指定值用Optional封装之后返回,如果该值为null,则抛出一个NullPointerException异常 |
ofNullable | 将指定值用Optional封装之后返回,如果该值为null,则返回一个空的Optional对象 |
or | 如果值存在,就返回同一个Optional对象,否则返回由支持函数生成的另一个Optional对象 |
orElse | 如果有值则将其返回,否则返回一个默认值 |
orElseGet | 如果有值则将其返回,否则返回一个由指定的Supplier接口生成的值 |
orElseThrow | 如果有值则将其返回,否则抛出一个由指定的Supplier接口生成的异常 |
stream | 如果有值,就返回包含该值的一个Stream,否则返回一个空的Stream |
8、Optional两个使用示例
避免如果Map中不含指定的键对应的值,它的get方法就会返回一个null
Optional<Object> aa = Optional.ofNullable(map.get("aa"));
Integer.parseInt(String),将String转换为int。在这个例子中,如果String无法解析到对应的整型
该方法就抛出一个NumberFormatException。最后的效果是,发生String无法转换为int时,代码发出一个遭遇非法参数的信号,
唯一的不同是,这次你需要使用try/catch语句,而不是使用if条件判断来控制一个变量的值是否非空
public static Optional<Integer> stringToInt(String s)
{
try{
//如果String能转换为对应的Integer,将其封装在Optional对象中返回
return Optional.of(Integer.parseInt(s));
}catch (NumberFormatException e){
//否则返回一个空的Optional对象
return Optional.empty();
}
}