Lambda Optional使用
简述
Optional 针对空指处理而设计的类型。
Java 8借鉴了Scala
和Haskell
,提供了一个新的Optional模板,可以用它来封装可能为空的引用。这是一个可以为null
的容器对象。
使用 Optional 的好处是可以以一种专门针对null的处理方式,来避免值可能存在 null 导致出现的程序异常。你可以理解为:处理null,就是你的业务。
在实际使用过程中,你会发现 Optional 的灵活性有时候会让你想用在任何可能出现null的地方,不过凡事都有套路可循,只要清楚利弊就知道该如何选择。
从两个方面说一下 Optional
- 常用API
- 项目使用套路
只讲 API 不讲使用套路的文章都是耍流氓。
API说明
先看一下常用API,后面再讲实际使用场景。
- 构建API: 构建Optional对象:of()、ofNullable()、empty()
- 获取API: 获取Optional对象包装的值:get()、orElse()、orElseGet()、orElseThrow()
- 判断API:对Optional对象里包装的值做一些逻辑判断:isPresent()、ifPresent()、filter()
- 转换API:将Optional对象里包装的值转换成一个新的值:map()、flatMap();
构建API
Optional.of()
作用:构建 Optional 对象,不允许传入的值为
null
,传入就 null 马上抛异常。
这个API要慎用,一般在使用 Optional 时,就是要防住 null,这个API 上来就直接抛异常,一点机会也不给。
1 | public static void testOf() { |
Optional.ofNullable()
常用API。
允许传入的值为 null,如果值为 null,返回一个空的 Optional 传入 null 并不抛异常。
使用 Optional.get() 获取值时,有值正常返回,值为 null 抛异常。
1 | public static void testOfNullable() { |
Optional.empty()
作用:创建一个空的 Optional 对象,一般很少直接这样写,都是通过 ofNullable 直接接住变量。
empty()
方法创建的对象没有值,如果对 emptyOpt 变量调用isPresent()
方法会返回false,
调用get()方法抛出NullPointerException
异常。
1 | public static void testEmpty() { |
获取API
这一组API很常用:get、orElse、orElseGet、orElseThrow。
get() 使用简单,后面三个简单一些业务逻辑。
Optional.get()
作用:获取 Optional 中的数据。
可以看上一个例子。使用 Optional 时,如查值是 null,get 会抛异常。
orElse()
作用:如果有值就返回不执行,否则如果值为null,也会执行orElse();
这种做用是相当于在特定场景下的用法可以用它来代替if..else..
来完成很简洁的逻辑判断。
看到 orElse 中只有一个String不能做别的事?当然不是,可以写一个方法,orElse调用该方法,就可以写其他代码。
1 | import java.util.Optional; |
结果
Su
Susan
orElseGet()
作用:入参为null时,才会执行 orElseGet()。
和orElse的区别:
在optional为空值的情况下orElse和orElseGet都会执行,当optional不为空时,orElseGet不会执行。
1 | import java.util.Optional; |
结果
Xiao
Molly
orElseThrow()
作有:当参数为空时,抛异常。
1 | import java.util.Optional; |
结果
java.lang.Exception: 参数为空
at com.test.OptionalTest.lambda$testOrElseThrow$2(OptionalTest.java:30)
at java.util.Optional.orElseThrow(Optional.java:290)
at com.test.OptionalTest.testOrElseThrow(OptionalTest.java:30)
at com.test.OptionalTest.main(OptionalTest.java:9)
判断API
Optional.isPresent()
作用:判断变量是否为null
这个比较常用,用来提前判断一下值是否为空。实际业务场景中,很参数传入的时候,程序是不知道是否为null的,可以使用这个先进行判断。
1 | public static void testIsPersent() { |
Optional.ifPresent()
很常有的API。
作用:如果值不为null,则可以执行后续操作。
看这段代码,如果值为null不会进入 ifPresent,如果不为null,则进入。这样就不需要if来判断这种特性很方便写一个流畅的工作流风格代码。
1 | public static void testIfPersent() { |
Optional.filter()
作用:条件过滤,根据条件过滤不满足条件的数据。
1 | public List<UserInfo> getUsers() { |
Optional.map()
作用:映射出新对象。map 时return 什么类型的数据,接收时就必须使用对应的泛型接住。
1 | public static void testMap() { |
结果:
入参为 val1 时
bb
入参为 val2 时
Exception in thread "main" java.util.NoSuchElementException: No value present
套路
使用 Optional 时,Optional.get() 如果值为 null,还是会抛异常,那使用 Optional 有什么意义。
Optional 能不能当作返回出参,返回给外部调用。
先说第一个问题,意义在于,Optional 本身不会为 null,不会在被调用时出现空指针而导致异常。由于是通过 Optional 包裹可能出现空值的对象,所以多了一层保护机制。
Optional 不建议做为返回值,至于为什么后面说。
套路1 不返回null
保证返回的数据中绝对不返回null,保证不会因为null引起不可预见的异常。
结合 orElse,来保证如果下面的 list 中查出的数据是null,就返回一个空的ArrayList。
这种写法简单实用。
1 | public List<User> getUsers() { |
套路2 先判断,后使用
业务中从一个Service中获得一个数据,那么先处理一下。
判断List是否为空
1 | public static void testService() { |
判断List是否为空,不为空执行后续
1 | public static void testService() { |
这两种写法,其实很相近,该怎么选择呢,简化一下代码,如果只有在有值的情况下才处理,使用 ifPresent 的处理,是最简洁的。
1 | public void test() { |
这两种方式都是用在处理没程不需要返回值的情况下。
套路3 结合 Stream 使用
开发中使用Stream应该是用的最多的,还是一样的套路,防止出现 List 为空。
1 | List<Person> personList = personService.getPersons(); |
总结
Optional 的功能主要是在保证参数不出现null,通过提供的API来实现,让代码更加健壮。
健壮的代码有助于提高系统的稳定性,是一种不可多得的处理手段。即使不使用 Optional,也需要保证,不直接将 null 返回给上一级调用在方法内处理掉null。
不能相信调用的方法是安全的,需要自己对null有安全的处理。
Optional 只是简化了null的操作,即使没有 Optional 也要对null的处理放在一个重点关注的位置。