概述
针对我们熟悉又深恶痛绝的空指针异常 NullPointException
,JDK8 新增了一个用于处理可能出现的空对象的类 Optionalt ,也就意味着。他可以理解为对对象的包装类,对原本的类进行了增强,结合 Lambda 表达式和函数式接口,我们可以安心的操作一些可能是 null 的对象,避免繁琐的空值检验。
举个例子:
我们常见过一些处理对象的方法,有些时候经常能见到这样的写法:
1 | public static Optional<TestBean> getBean() { |
有些时候可能是try-catch块的限制,或者一些其他什么原因,我们的方法可能会返回一个 null,这样对于调用方来说,如果没有做空值检验就很有可能报 NPE,使用 Optional,我们可以显示的声明返回值可能是 null,并且返回一个 Option 类,调用方可以直接使用内置的方法进行处理。
1 | public Optional<TestBean> getStr() { |
一、创建 Optional 对象
在开始了解如何创建 Optional 对象之前,我们要明确,和 Integer 一样,Option 分为类本身与成员变量 value,其中 value 是可能为 null。
创建 Optional 对象有三种方法:
Optional.empty()
方法:创建一个 value 为 null 的空值 Optional1
Optional<String> emptyOptional = Optional.empty();
Optional.of()
方法:创建一个有默认值的 Optional1
Optional<String> defaultOptional = Optional.of("这是一个默认值");
Optional.ofNullable()
方法:创建一个可能是空值的 Optional。当有值的时候等同于Optional.of()
,无值的时候等同于Optional.empty()
1
Optional<String> nullableOption = Optional.ofNullable(null);
Optional 的构造器是私有的,即无法也不应该通过构造方法去创建一个 Optional 实例。
下文的示例将基于空值对象 emptyOptional,非空值对象 defaultOptional 进行演示,
二、Optional 基本使用
1.判空
isPresent()
方法:判空用于判断 Optional 类所包装的 value 是否非空.1
2
3System.out.println(emptyOptional.isPresent()); // false
System.out.println(defaultOptional.isPresent()); // true
System.out.println(nullableOption.isPresent()); // falseifPresent()
方法:用于指定若值为不为空的情况下的处理方法,可以看做ifPresent()
用法的延伸1
emptyOptional.ifPresent(s -> System.out.println("这玩意不是null"));
2.获取值
获取值的方法:
orElse()
方法:参数是一个变量,允许传入空值而不会报 NPE。1
2System.out.println(emptyOptional.orElse(null)); // null
System.out.println(emptyOptional.orElse("这是空的")); // 这是空的orElseGet()
方法:参数是一个无参有返回值的函数式接口 Supplier,不允许传入空值,当传入空值的时候会报 NPE 异常。1
2System.out.println(emptyOptional.orElseGet(null)); // java.lang.NullPointerException
System.out.println(emptyOptional.orElseGet(() -> "aaa")); // aaa由于Supplier是无参的,所以更推荐使用
Class::method
这样形式的静态方法引用来传入方法。orElseThrow()
方法:与orElseGet()
方法有点像,不过在传入的一个抛出异常的方法,简而言之,就是如果空值就抛异常,传入 null 就抛出 NPE,否则就按传入的自定义异常的构造函数来跑异常。1
2System.out.println(nullableOption.orElseThrow(null)); // java.lang.NullPointerException
System.out.println(nullableOption.orElseThrow(MyException::new)); // MyExceptionget()
方法:Option 的get()
是一个奇葩,当 value 是 null 的时候,他不会抛出 NPE,但是会抛出NoSuchElementException
,实乃脱裤子放屁之举。所以一般不通过get()
去取值,只在内部用于其他方法的实现。1
System.out.println(emptyOptional.get()); // java.util.NoSuchElementException
orElse()
方法和 orElseGet()
方法用法看起来有点相近,但是参数决定了两个方法使用场景的不同。
就我个人理解,orElse()
适用于检验参数的非空,比如一些非必要参数可以通过该方法保证默认值;而orElseGet()
因为传入的是一个函数式接口的实现,适用于在使用前需要对可能为空的值进行预选处理的场合,比如日志系统记录空查询这样的情况。
另外,值得一提的是,toString
未尝不能看做是一种特别的取值行为。Optional 重写了 toStirng()
方法,当 value 为 null 的时候返回的是字符串 "Optional.empty",否则返回 "Optional[默认值]"形式的字符串
1 | // 空值打印 Optional.empty,否则打印 Optional[这是一个默认值] |
三、Optional 过滤和转换
1.过滤
Optional 提供了 filter()
方法用于过滤数据。它和 Stream 的 filter()
方法一样,都是使用了有一个参数返回boolean值的函数式接口 Predicate作为参数,通过 Lambda 表达式直接传入实现类,从而实现快速过滤的目的。
举个例子:
1 | // emptyOptional里面字符串长度并没有大于5 |
由于默认会进行一次判空,所以filter
我们可以看成下面代码的简化:
1 | if (conditionStr != null) { |
2.转换
map()
用于值的转换,这个方法同样与 Stream 的 map()
异曲同工。通过传入拥有一个参数和一个返回值的函数式接口 Function 的实现类,从而实现 value 的转换。
举个例子:
1 | // defaultOptional的value为“这是一个默认值” |
四、总结
网上对 Optional 类的评价有褒有贬,公司里因为有专门用于处理各种类型的参数的空值工具类,所以也不太常见到过它。但是我在知乎上曾经看到过一个对 Optional 类的很贴切的总结:
Optional的核心思想就是我明确告诉你可能会返回null,你一定要处理。所以,现在模块间提供给其他人的接口,如果有可能返回为null都要声明为Optional。