• Java8系列 (五) Optional类


    概述

    在Java8之前, 如果需要对一个变量做一次 null 检查, 通常会像下面这样写

        T t = service1.query();
        if (t != null) {
            K k = service2.update(t);
            if (k != null) {
                U u = service3.save(k);
            }
        }

    如果业务比较复杂, 可能会像上面那样, 使用 if 语句进行多层嵌套, 后期难以扩展。

    在Java8中新引入了一个 Optional 类, Optional 类会对可能为 null 值的变量进行建模, 这样你就不必直接将 null 赋值给变量, 也就不必像上面那样进行多层嵌套检查。

    在正式介绍 Optional 类的 API之前, 先引入一些实体类, 后面的代码示例会经常用到它们。

    @Data
    public class Person {
    
        private Car car;
        private int age;
    
        public Optional<Car> getCar() {
            return Optional.ofNullable(car);
        }
    }
    
    @Data
    public class Car {
    
        private Insurance insurance;
    
        public Optional<Insurance> getInsurance() {
            return Optional.ofNullable(insurance);
        }
    }
    
    @Data
    public class Insurance {
    
        private String name;
    }
    View Code

    创建Optional对象

    Optional 类提供了三种方式来创建 Optional 对象

    • empty(): 创建一个空的 Optional 对象
    • of(): 将指定值用 Optional 封装之后返回,如果该值为 null ,则抛出一个 NullPointerException 异常
    • ofNullable(): 将指定值用 Optional 封装之后返回,如果该值为 null ,则返回一个空的 Optional 对象
        @Test
        public void test1() {
            //声明一个空的optional
            Optional<Car> c1 = Optional.empty();
            //根据一个非空的值创建optional, 如果传入的值是null,会抛出空指针异常
            Optional<Car> c2 = Optional.of(new Car());
            //可接受null的Optional
            Optional<Object> c3 = Optional.ofNullable(null);
        }

    flatMap和map用法

    flatMap()和map()都是在指定值存在时, 就对该值执行提供的 mapping 函数调用, 返回一个 Optional 类型的值, 否则就返回一个空的 Optional 对象。

    注意 flatMap() 方法的转换函数是 Function<? super T, Optional<U>> mapper , 这个特性和前面介绍的 Stream 类的 flatMap() 很像。

    查看一下 Optional 类的源码, flatMap() 会将 Optional<T> 中的 T 转换成 Optional<U>, 再将转换后的 Optional<U> 直接作为方法返回值返回。

        @Test
        public void test2() {
            Optional<Person> person = Optional.ofNullable(new Person());
            String name = person.flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("哪吒之魔童降世");
            System.out.println(name); //result: 哪吒之魔童降世
        }

    上面的代码示例中,  因为 Person 实体类中 car 属性是一个 null 值, 所以  person.flatMap(Person::getCar) 这个操作会得到一个空的Optional 对象。

    这样就导致了后面的 flatMap() 和 map() 两次转换都返回一个空的 Optional 对象。orElse() 方法的含义是如果有值则将其返回,否则返回一个默认值。所以最后的输出结果是默认值  哪吒之魔童降世 。

    在实际业务中, 你可以像下面这样将 Optional 作为方法入参和返回值使用

        @Test
        public void test3() {
            Optional<Insurance> insurance1 = find(Optional.of(new Person()), Optional.empty());
            System.out.println(insurance1);//result: Optional.empty
            Optional<Insurance> insurance2 = find(Optional.of(new Person()), Optional.of(new Car()));
            System.out.println(insurance2);//result: Optional[Insurance(name=null)]
        }
    
        public Optional<Insurance> find(Optional<Person> person, Optional<Car> car) {
            return person.flatMap(p -> car.map(c -> find(p, c)));
        }
    
        private Insurance find(Person p, Car c) {
            return new Insurance();
        }

    filter和ifPresent

    • filter(): 如果值存在并且满足提供的谓词, 就返回包含该值的 Optional 对象; 否则返回一个空的 Optional 对象
    • ifPresent(): 如果值存在, 就执行使用该值的方法调用, 否则什么也不做
        @Test
        public void test4() {
            Optional.of("攀登者").filter(i -> i.length() < 4).ifPresent(System.out::println);//打印: 攀登者
            Optional.<String>empty().filter(i -> i.length() < 4).ifPresent(System.out::println);//没有输出
        }

    下面是一个测试示例, 到本地去运行看下输出结果, 它可以帮你重新理解一下前面介绍的几个 API 的用法。

        @Test
        public void test5() {
            System.out.println(getCarInsuranceName(Optional.empty(), 8));//result: 命运之夜
            Person p = new Person();
            p.setAge(10);
            System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 命运之夜
            Insurance insurance = new Insurance();
            insurance.setName("知否知否");
            Car car = new Car();
            car.setInsurance(insurance);
            p.setCar(car);
            System.out.println(getCarInsuranceName(Optional.of(p), 8));//result: 知否知否
        }
    
        //找出年龄大于或者等于 minAge 参数的 Person 所对应的保险公司列表。
        public String getCarInsuranceName(Optional<Person> person, int minAge) {
            return person.filter(p -> p.getAge() >= minAge).flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("命运之夜");
        }

    Optional与异常的对比

    通常在由于某种因素下, 函数无法正确返回某个值, 常见的做法就是使用 try/catch 语句处理返回一个null值, 或者不做任何处理直接抛出一个异常。

    如果函数无法正确返回某个值, 且你不需要它抛出异常, 而是要它返回一个默认值, 那么 Optional 可以帮您更优雅的实现

        @Test
        public void test6() {
            String value = "test";
            Integer res = Optional.of(value).flatMap(this::str2Int).filter(i -> i > 0).orElse(0);
            System.out.println(res);//result: 0
        }
    
        private Optional<Integer> str2Int(String str) {
            try {
                //如果能正确解析, 就将其封装在 Optional 中返回
                return Optional.of(Integer.valueOf(str));
            } catch (NumberFormatException ex) {
                //如果解析发生异常, 就返回一个空的 Optional
                return Optional.empty();
            }
        }

    上面的代码清单,   Integer.valueOf() 会在入参不是一个整型数值时, 抛出 NumberFormatException , 我们这里对它做了处理, 当解析发生异常时, 就返回一个空的 Optional 对象。

    上面的用例只有在 value 变量是大于零的整型数值时, 才会输出 value 变量的值, 否则, 输出结果都是默认值 0。

    总结

    最后, 来一张大图

    参考资料

    Java8 实战

    作者:张小凡
    出处:https://www.cnblogs.com/qingshanli/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】。

  • 相关阅读:
    ModelMap和ModelAndView的作用
    jquery 临时存值
    条件sql ibatis
    IBATIS动态SQL
    MyEclipse 快捷键
    jquery $用法
    弹窗案例
    Ibatis sql语句1
    Ibatis sql语句
    jQuery的三种$()
  • 原文地址:https://www.cnblogs.com/qingshanli/p/11783499.html
Copyright © 2020-2023  润新知