0
点赞
收藏
分享

微信扫一扫

Java8中Optional类入门-替代null避免冗杂的非空校验


场景

Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验:

​​Java核心工具库Guava介绍以及Optional和Preconditions使用进行非空和数据校验_霸道流氓气质的博客博客​​

上面在讲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,如果保险不为空,则获取保险名称。

注:


实现

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);

各阶段的类型明细为

Java8中Optional类入门-替代null避免冗杂的非空校验_System

 

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();
}
}

举报

相关推荐

0 条评论