首页 > 编程语言 >Java8中Optional类入门-替代null避免冗杂的非空校验

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

时间:2022-12-27 17:26:23浏览次数:64  
标签:map 非空 对象 car Insurance null Optional public

场景

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

https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/127683387

上面在讲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();
        }
    }

 

标签:map,非空,对象,car,Insurance,null,Optional,public
From: https://www.cnblogs.com/badaoliumangqizhi/p/17008526.html

相关文章