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;
}
创建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。
总结
最后, 来一张大图

参考资料
作者:张小凡
出处:https://www.cnblogs.com/qingshanli/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。如果觉得还有帮助的话,可以点一下右下角的【推荐】。
Java8系列 (五) Optional类的更多相关文章
- Java8 新特性 Optional 类
Optional 类的简介 Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常. Optional ...
- Java基础复习笔记系列 五 常用类
Java基础复习笔记系列之 常用类 1.String类介绍. 首先看类所属的包:java.lang.String类. 再看它的构造方法: 2. String s1 = “hello”: String ...
- Java8新特性——Optional类的使用(有效的避免空指针异常)
OPtional类的使用 概述 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guav ...
- 使用Java8中的Optional类来消除代码中的null检查
简介 Optional类是Java 8新增的一个类,Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException). —— 每个 Java 程序员都非常了解的异常 ...
- Java多线程系列五——列表类
参考资料: http://xxgblog.com/2016/04/02/traverse-list-thread-safe/ 一些列表类及其特性 类 线程安全 Iterator 特性 说明 Vect ...
- Java8新特性——Optional
前言 在开发中,我们常常需要对一个引用进行判空以防止空指针异常的出现.Java8引入了Optional类,为的就是优雅地处理判空等问题.现在也有很多类库在使用Optional封装返回值,比如Sprin ...
- Java8新特性之空指针异常的克星Optional类
Java8新特性系列我们已经介绍了Stream.Lambda表达式.DateTime日期时间处理,最后以"NullPointerException" 的克星Optional类的讲解 ...
- Java8 Lambda表达式、Optional类浅析
1.概念 Lambda是一个匿名函数,可以将其理解为一段可以传递的代码(将代码像数据一样进行传递)可以写出更简洁.更灵活的代码.作为一种更紧凑的代码风格,使得java语言的表达能利得到了提升. 2. ...
- Java8之Optional类
写在前头 今天再看阿里的Java开发手册,里面异常处理第10条提到这样一个建议. [推荐]防止 NPE ,是程序员的基本修养,注意 NPE 产生的场景:1 ) 返回类型为基本数据类型,return 包 ...
随机推荐
- HashMap和Hashtable的联系和区别
实现原理相同,功能相同,底层都是哈希表结构,查询速度快,在很多情况下可以互用,早期的版本一般都是安全的. HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分 ...
- Marshmallow权限使用
Google发布Android 6.0后对用权限的控制更加严格,在Android5.1或更低的版本中用户能在App的安装期间或使用设置应用程序权限来同意或拒绝某个权限,而在Android6.0或更高的 ...
- PyCharm中创建项目时,在所创建的python虚拟环境下的pip失效
在这篇博文里,我简单地叙述了我在使用PyCharm创建一个flask项目时遇到的问题,以及我解决这个问题的过程.其中比较值得注意的点有:①PyCharm创建新项目时的解释器配置②Python虚拟环境的 ...
- jQuery常用方法(三)-jQuery Ajax
JQuery Ajax 方法说明: load( url, [data], [callback] ) 装入一个远程HTML内容到一个DOM结点. $("#feeds").load(& ...
- [Abp vNext 源码分析] - 9. 接口参数的验证
一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...
- CentOS7 Redis5.0.5环境搭建
CentOS7 Redis5.0.5环境搭建 1基本环境配置 CentOS Linux release 7.6.1810 (Core) redis 5.0.5 1.下载解压redis.通过wget在官 ...
- 深入理解SpringCloud之Gateway
虽然在服务网关有了zuul(在这里是zuul1),其本身还是基于servlet实现的,换言之还是同步阻塞方式的实现.就其本身来讲它的最根本弊端也是再此.而非阻塞带来的好处不言而喻,高效利用线程资源进而 ...
- 注册中心nacos完整部署及与eureka区别
1. 场景描述 nacos最近用的比较多,介绍下nacos及部署吧,刚看了下以前写过类似的,不过没写如何部署及与eureka区别,只展示了效果,补补吧. 2.解决方案 2.1 nacos与eureka ...
- BZOJ 1965 [AHOI2005]洗牌
题目描述 为了表彰小联为Samuel星球的探险所做出的贡献,小联被邀请参加Samuel星球近距离载人探险活动. 由于Samuel星球相当遥远,科学家们要在飞船中度过相当长的一段时间,小联提议用扑克牌打 ...
- Springboot】Springboot整合邮件服务(HTML/附件/模板-QQ、网易)
介绍 邮件服务是常用的服务之一,作用很多,对外可以给用户发送活动.营销广告等:对内可以发送系统监控报告与告警. 本文将介绍Springboot如何整合邮件服务,并给出不同邮件服务商的整合配置. 如图所 ...