Java8中Optional操作的实际应用
作者:汤圆学Java 发布时间:2022-04-30 22:52:31
简介
目的:Optional的出现主要是为了解决null指针问题,也叫NPE(NullPointerException)
外形:Optional外形酷似容器(其实它就是一个容器),只是这个容器比较特殊,因为它只能存放一个对象,运气不好的话这个对象还是个null
操作:Optional从操作上来看,又跟前面的Stream流式操作很像,比如过滤filter - 提取map等
下面我们用比较简单的例子来对比着看下,Optional的一些基础用法
1. Optional是什么
Optional是一个容器,只能存放一个对象(可为null)
Optional的出现是
一个是为了解决NPE问题(阿里开发手册也有提到这一点,点击可直接下载,官方链接)
另一个是为了代码更加清晰可读,因为Optional这个名字的灵感就是来自英文optional(可选的),意思就是说这个对象可以为空,可以不为空
2. 没它 VS 有它
下面我们用旧代码和新代码来对比着看(所谓的新旧是以Java8为分割线)
案例1:现有C类,我们要提取C.name属性
public class OptionalDemo {
private static final String DEFAULT_NAME = "javalover";
public static void main(String[] args) {
// 传入null,以身试法
getName(null);
}
// 取出c.name
public static void getName(C c){
// 旧代码 Java8之前
String name = (c!=null ? c.getName() : DEFAULT_NAME);
System.out.println("old: "+name);
// 新代码 Java8之后(下面的三个操作方法后面会介绍,这里简单了解下)
String nameNew = Optional
// 工厂方法,创建Optional<C>对象,如果c为null,则创建空的Optional<C>对象
.ofNullable(c)
// 提取name,这里要注意,即使c==null,这里也不会抛出NPE,而是返回空的Optional<String>,所以在处理数据时,我们不需要担心空指针异常
.map(c1->c1.getName())
// 获取optional的属性值,如果为null,则返回给定的实参DEFAULT_NAME
.orElse(DEFAULT_NAME);
System.out.println("new: "+nameNew);
}
}
class C{
private String name;
public C(String name) {
this.name = name;
}
// 省略getter/setter
}
乍一看,好像Java8之前的旧代码更合适啊,只需要一个三目运算符
再看Optional操作,发现并没有那么简洁
是这样的,如果只是一层判断,那普通的if判断做起来更方便;
但是如果嵌套两层呢,比如b.getC().getName()?
下面我们就看下,两层嵌套会怎么样
例子2:现多了一个B类(依赖C类),我们要从对象B中提取C的属性name,即b.getC().getName()
public static void getName2(B b){
// 旧代码
String name = (b!=null ? ( b.getC()!=null ? b.getC().getName() : DEFAULT_NAME) : DEFAULT_NAME);
// 新代码
String nameNew = Optional
.ofNullable(b)
.map(b1->b1.getC())
.map(c1->c1.getName())
.orElse(DEFAULT_NAME);
System.out.println(nameNew);
}
class B{
private C c;
public B(C c) {
this.c = c;
}
// 省略getter/setter
}
这次不管是乍一看,还是一直看,都是Optional更胜一筹
例子3:现多了一个A类(依赖B类),我们要提取a.getB().getC().getName()
等等等,省略号
意思到就行,反正要说的就是单从判空来看的话,Optional肯定是好过三目运算符的(if/else这里就不举了,它的嵌套只会更多)
3. 核心操作
因为Optional主要是操作数据(类似数据库操作),所以我们这里从数据的角度来进行分析
这里我们可以分为三种操作:保存数据、处理数据、获取数据
保存数据:
(没有默认值)
public static <T> Optional<T> of(T value)
:填充 T value 到 Optional 的属性中;如果 value==null,则抛出NPE(默认null)
public static <T> Optional<T> ofNullable(T value)
:填充 T value 到 Optional 的属性中;如果引用为null,则填充null(构造一个空的Optional)
public static<T> Optional<T> empty()
:单纯地创建一个数据为null的空Optional,即直接填充null到 Optional 的属性中【不常用】
处理数据:
(提取)
public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
:提取Optional中属性T的某个属性值U,并将U填充到新的Optional中并返回(过滤)
public Optional<T> filter(Predicate<? super T> predicate)
:过滤Optional中属性T的某个属性值,符合条件则将T填充到新的Optional中并返回(扁平化提取)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
:提取Optional中属性T的某个属性Optional<U>
,直接返回
获取数据:
public T orElse(T other)
:获取数据,如果数据为null,则返回T otherpublic T orElseGet(Supplier<? extends T> other)
:获取数据,如果数据为null,则通过函数式接口other返回一个新的数据Tpublic T get()
:获取数据,如果数据为null,则报NPE【不常用】
上面这些操作中,不常用的就是get()和empty()
其他的就不举了,这里主要说下map()和flatMap()
如下图所示:
map()主要是提取Optional中的属性C的属性name,然后再包装到新的Optional
输入Optional<C>
, 输出Optional<String>
(即Optional<c.name>)
String nameNew = Optional
.ofNullable(c)
.map(c1->c1.getName())
.orElse("xxx");
flatMap()主要是提取Optional中的属性B的Optional<C>
属性中的C的值,然后再包装到新的Optional
输入Optional<B>
,输出Optional<C>
public class FlatMapDemo {
private static final String DEFAULT_NAME = "javalover";
public static void main(String[] args) {
getName(null);
}
// 取出 b.c.name
public static void getName(B b){
C c = Optional
.ofNullable(b)
// 这里扁平化处理,提取Optional<C>中的C
// 如果用map,则返回的是Optional<Optional<C>>
.flatMap(b->b.getC())
.orElse(new C("xxx"));
System.out.println(c.getName());
}
}
class B{
private Optional<C> c;
public Optional<C> getC() {
return c;
}
public void setC(C c) {
this.c = Optional.ofNullable(c);
}
}
class C{
private String name;
public C(String name) {
this.name = name;
}
// 省略getter/setter
}
4. 应用
从规范角度来讲,是为了代码清晰,一看用Optional<T>
变量,就知道T可能为null;
从编码角度来讲,主要是应用在非空判断;但是实际场景的话,有两个
没有用Optional进行包裹的参数:比如上面讲到的例子,传来的参数就是普通对象,我们就需要自己用Optional容器来包裹传来的参数,然后进行后续操作
// 取出c.name
public static void getName(C c){
// 自己手动包装 Optional<C>
String nameNew = Optional
.ofNullable(c)
.map(c1->c1.getName())
.orElse(DEFAULT_NAME);
System.out.println("new: "+nameNew);
}
有用Optional进行包裹的参数:比如数据库查询时,我们可以用Optional来包裹查询的结果并返回,这样我们分析结果的时候,只需要通过orElse()来获取,同时还可以设定默认值
// 返回Optional<Car>,通过.orElse(defaultCar)就可以获取返回值,如果返回值为null,还可以设定一个默认值defaultCar
Optional<Car> selectOne(SelectStatementProvider selectStatement);
来源:https://juejin.cn/post/6953219282029412366


猜你喜欢
- 解决Spring in action @valid验证不生效按照书上的示例代码来实现但是,添加了验证但是没有生效。Spring提供了校验Ap
- 在我们编写好一款软件后,我们不想别人盗用我们的软件,这时候我们可以采用注册的方式来保护我们的作品。这时候我们可能就需要简单了解一下加密解密技
- 目录1、java有8种基本类型,请问byte、int、long、char、float、double、boolean各占多少个字节?2、在 A
- 这篇文章主要介绍了springboot整合通用mapper过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价
- Transactional事务的使用及注意Transactional的事务使用,主要引用两个包中的Bean,一个是jpa的javax.tra
- 需求:Android调用webView加载网页的时候,拦截某一个链接不执行此链接,执行指定跳转到其他activity页面。webview的s
- ZXing是谷歌的一个开源库,可以用来生成二维码、扫描二维码。本文所介绍的是第一部分。首先上效果图:ZXing相关各种文件官方下载地址:ht
- 本文实例展示了WinForm实现为TextBox设置水印文字功能,非常实用的技巧,分享给大家供大家参考。关键代码如下:using Syste
- 从接触springboot开始,便深深的被它的简洁性深深的折服了,精简的配置,方便的集成,使我再也不想用传统的ssm框架来搭建项目,一大堆的
- 本文主要探究的是关于Bean的作用域、生命周期的相关内容,具体如下。Bean的作用域Spring 3中为Bean定义了5中作用域,分别为si
- 本文实例讲述了java实现简单的英文文本单词翻译器功能。分享给大家供大家参考,具体如下:直接上代码:package fanyi;import
- 一、File类1、简介java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关 File 能新建、删除、重命名文件和目录,但
- 动态内存管理为什么存在动态内存分配我们到现在为止掌握的是什么样的内存开辟方式呢//创建一个变量int val = 20; &n
- 本文实例为大家分享了AJAX二级联动效果的具体代码,供大家参考,具体内容如下Ajax.jsvar createAjax = function
- 背景:在android开发中,列表是经常会使用到的一个主要控件,列表中可以展示大量的数据,像订单、商品、通讯录、浏览记录或者关注列表等等。可
- android root权限破解分析许多机友新购来的Android机器没有破解过Root权限,无法使用一些需要高权限的软件,以及进行一些高权
- 本文实例讲述了C#堆排序实现方法。分享给大家供大家参考。具体如下:private static void Adjust (int[] lis
- 目录1 简介2 项目整合2.1 JWT整合2.1.1 JWT工具类2.1.2 Token处理的Filter2.1.3 JWT属性2.2 Sp
- 今天写Tab的时候由于TAB的跳转问题去查资料,倒反而发现更有趣的问题,就是如何将TAB放置在屏幕的底端。 <?xml version
- 在工作过程中,需要将一个文件夹生成压缩文件,然后提供给用户下载。所以自己写了一个压缩文件的工具类。该工具类支持单个文件和文件夹压缩。放代码: