深入学习Optional类 - 消灭空指针异常

前言:

空指针异常在Java中的出现就好像死亡在你生命中一样,你无法避免也无法拒绝。不可否认null对象在编程上给我们带来很多便利,但在便捷的同时,要求开发人员仔细检查、校验。相信我们在写代码中的时候都写过无数个if xx = null ...这种代码,这篇博客就是教你使用Optional代替没有营养的校验,然后开启你的函数式编程

  • 空指针引用:价值十亿美元的错误

我称之为我的十亿美元错误……当时,我正在设计第一个全面的类型系统,用于面向对象语言中的引用功能。我的目标是确保所有对引用的使用都是绝对安全的,由编译器自动执行检查。但是我无法拒绝定义一个 Null 引用的诱惑,因为它实在太容易实现了。这导致了无数错误、漏洞和系统崩溃。在过去的四十年里,这些问题可能已经造成了十亿美元的损失。 ——托尼·霍尔,ALGOL W 的发明者。

1.Optional介绍

Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。

我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException:

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

在上面的任何一个调用都有可能抛出空指针异常,那么我们就需要做判断

   // 查询国家码
    public String getIsoCode(User user) {
        if (user != null) {
            Address address = user.getAddress();
            if (address != null) {
                Country country = address.getCountry();
                if (country != null) {
                    String isocode = country.getIsocode();
                    if (isocode != null) {
                        isocode = isocode.toUpperCase();
                        return isocode;
       
                    }
                }
            }
        }
        return "null";
    }

为了不再嵌套if语句判断,外加没意义没营养的取值,我们使用Optional来看看效果

    public String getIsoCode(User user) {
        return Optional.ofNullable(user)
                .map(a -> a.getAddress())
                .map(a -> a.getCountry())
                .map(a -> a.getIsocode())
                .map(a -> a.toUpperCase())
                .orElse("CHINA");
    }

2.Optional使用

2.1 创建Optional对象

这个类的构造方法是私有的,同时他也无法被继承,所以要创建该对象只能使用它内置的两个静态方法 of、ofNullable或者empty方法创建value为null的Optional对象。

        // 如果为空抛出空指针异常
        Optional.of(null);
        // 如果为空插入value设置为空
        Optional.ofNullable(null);
        // 创建空的对象
        Optional.empty()

2.2 isPresent 、ifPresent

从字面意思两者极其相似,都是判断是否存在,但通过源码,还是能看到区别;

isPresent:判断该对象的value是否存在,没有入参,返回值boolean ifPresent:判断该对象的value是否存在,如果存在执行传入的函数,入参为对应的函数实现,无返回值

		User user = new User("endwas", 45);
        // 返回值判断value是否不为空
        boolean present = Optional.ofNullable(user.getName()).isPresent();
        // 当value不为空时候才对该value执行函数式编程
        Optional.ofNullable(user.getName()).ifPresent(System.out::println);

2.3 orElse、orElseGet、orElseThrow

这两个方法从意思上也挺类似,都是在value为空时,我们要怎么处理;

orElse是当value的值为null时,返回传入的值orElseGet则是当value的值为null时,返回函数式方法执行结果orElseThrow是当value值为null时,抛出传入的异常

 		User user = new User(null, 45);
        // value不为null时返回value,否则返回orElse内的值,【注:两者必须同一类型
        String orElse = Optional.ofNullable(user.getName()).orElse("orElse");

        // value不为null时返回value,否则调用orElse方法,orElse无入参有返回值为传入的类型
        String orElseGet = Optional.ofNullable(user.getName()).orElseGet(() -> {
            String returnValue = "retrun orElseGet";
            return returnValue;
        });

        String orElseThrow = Optional.ofNullable(user.getName()).orElseThrow(()->new RuntimeException("errororor"));
        System.out.println(orElseThrow);

2.4 filter

filter就是过滤器,他传入的也是一个函数式方法,他的作用就是对value进行过滤,如果满足过滤条件true则返回原Optional对象,反之返回empty对象;类似stream中的filter对象

		User user = new User("endwas", 45);
		// 对user.name进行过滤如果以“end”开头则返回原对象,否则返回空,然后打印
        Optional.of(user.getName()).filter(value -> value.startsWith("end")).ifPresent(System.out::println);

2.5 map、flatMap

map()和flatMap()对Optional中的对象进行转换值的操作,这两个方法唯一的区别就是接受的参数不同。 注意:value不能为null,否则直接返回empty不会执行map方法

map()方法的mapping函数返回值可以是任何类型T,然后在map中封装好返回,而flatMap()方法的mapping函数则是你需要在函数方法中封装好Optional,然后返回该对象。

 		User user = new User("endwas", 45);
        // map返回值为任意类型,然后把它封装成Optional对象
        Optional.of(user).map(u -> u.getName()).ifPresent(System.out::println);
        // flatMap返回值为
        Optional.of(user).flatMap(u -> Optional.ofNullable(u.getName())).ifPresent(System.out::println);

总结:

  1. Optional本质是为了解决日常开发中对象判断为null情形,使用链式开发,更加优雅。
  2. Optional内部封装了value属性,同时对value使用,进行了严格判定,避免空指针异常情形
  3. 当然了,如果你想遇到null,直接抛出异常,不妨使用Objects.requireNonNull(comparator);
end
  • 作者:Endwas(联系作者)
  • 发表时间:2021-03-31 15:48
  • 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)
  • 转载声明:如果是转博主转载的文章,请附上原文链接
  • 公众号转载:请在文末添加作者名字和博客地址
  • 评论