Optional的正确打开方式
前言
Optional类是Java 8新增的一个类,用以解决程序中常见的NullPointerException异常问题。本篇文章将详细介绍Optional类,以及如何用它消除代码中的null检查。
避免使用null检查
作为Java开发人员,几乎所有人都遇到过NullPointerException异常,大多数人遇到NullPointerException异常时都会在异常出现的地方加上if代码块来判断值不为空,比如下面的代码:
public void bindUserToRole(User user) {if (user != null) {String roleId = user.getRoleId();if (roleId != null) {Role role = roleDao.findOne(roleId);if (role != null) {role.setUserId(user.getUserId());roleDao.save(role);}}}
}
这是比较普遍的做法,为了避免出现NullPointerException异常,手动对可能为null值进行了处理,不过代码看起来非常糟糕,业务逻辑被淹没在if逻辑判断中,也许下面的代码看起来可读性稍好一些:
public String bindUserToRole(User user) {if (user == null) {return;}String roleId = user.getRoleId();if (roleId == null) {return;}Role = roleDao.findOne(roleId);if (role != null) {role.setUserId(user.getUserId());roleDao.save(role);}
}
上面的代码避免了深层的if语句嵌套,但本质上是一样的,方法内有三个不同的返回点,出错后调试也不容易,因为你不知道是那个值导致了NullPointerException异常。基于上面的原因,Java 8中引入了一个新的类Optional,用以避免使用null值引发的种种问题。
Optional类
java.util.Optional<T>
类是一个封装了Optional值的容器对象,Optional值可以为null,如果值存在,调用isPresent()方法返回true,调用get()方法可以获取值。
创建Optional对象
Optional类提供三个方法用于实例化一个Optional对象,它们分别为empty()、of()、ofNullable(),这三个方法都是静态方法,可以直接调用。
empty()方法用于创建一个没有值的Optional对象:
Optional<String> emptyOpt = Optional.empty();
empty()方法创建的对象没有值,如果对emptyOpt变量调用isPresent()方法会返回false,调用get()方法抛出NullPointerException异常。
of()方法使用一个非空的值创建Optional对象:
String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);
ofNullable()方法接收一个可以为null的值:
Optional<String> nullableOpt = Optional.ofNullable(str);
如果str的值为null,得到的nullableOpt是一个没有值的Optional对象。
提取Optional对象中的值
如果我们要获取User对象中的roleId属性值,常见的方式是直接获取:
String roleId = null;
if (user != null) {roleId = user.getRoleId();
}
使用Optional中提供的map()方法可以以更简单的方式实现:
Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);
使用orElse()方法获取值
Optional类还包含其他方法用于获取值,这些方法分别为:
orElse():如果有值就返回,否则返回一个给定的值作为默认值;
orElseGet():与orElse()方法作用类似,区别在于生成默认值的方式不同。该方法接受一个Supplier<? extends T>函数式接口参数,用于生成默认值;
orElseThrow():与前面介绍的get()方法类似,当值为null时调用这两个方法都会抛出NullPointerException异常,区别在于该方法可以指定抛出的异常类型。
下面来看看这三个方法的具体用法:
String str = "Hello World";
Optional<String> strOpt = Optional.of(str);
String orElseResult = strOpt.orElse("Hello Shanghai");
String orElseGet = strOpt.orElseGet(() -> "Hello Shanghai");
String orElseThrow = strOpt.orElseThrow(() -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));
此外,Optional类还提供了一个ifPresent()方法,该方法接收一个Consumer<? super T>函数式接口,一般用于将信息打印到控制台:
Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);
使用filter()方法过滤
filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤:
Optional<String> optional = Optional.of("lw900925@163.com");
optional = optional.filter(str -> str.contains("164"));
在上面的代码中,如果filter()方法中的Lambda表达式成立,filter()方法会返回当前Optional对象值,否则,返回一个值为空的Optional对象。Java知音公众号内回复“后端面试”,送你一份Java面试题宝典。
如何正确使用Optional
通过上面的例子可以看出,Optional类可以优雅的避免NullPointerException带来的各种问题,不过,你是否真正掌握了Optional的用法?假设你试图使用Optional来避免可能出现的NullPointerException异常,编写了如下代码:
Optional<User> userOpt = Optional.ofNullable(user);
if (userOpt.isPresent()) {User user = userOpt.get();// do something...
} else {// do something...
}
坦白说,上面的代码与我们之前的使用if语句判断空值没有任何区别,没有起到Optional的正真作用:
if (user != null) {// do something...
} else {// do something...
}
当我们从之前版本切换到Java 8的时候,不应该还按照之前的思维方式处理null值,Java 8提倡函数式编程,新增的许多API都可以用函数式编程表示,Optional类也是其中之一。这里有几条关于Optional使用的建议:
尽量避免在程序中直接调用Optional对象的get()和isPresent()方法;
避免使用Optional类型声明实体类的属性;
第一条建议中直接调用get()方法是很危险的做法,如果Optional的值为空,那么毫无疑问会抛出NullPointerException异常,而为了调用get()方法而使用isPresent()方法作为空值检查,这种做法与传统的用if语句块做空值检查没有任何区别。
第二条建议避免使用Optional作为实体类的属性,它在设计的时候就没有考虑过用来作为类的属性,如果你查看Optional的源代码,你会发现它没有实现java.io.Serializable接口,这在某些情况下是很重要的(比如你的项目中使用了某些序列化框架),使用了Optional作为实体类的属性,意味着他们不能被序列化。
下面我们通过一些例子讲解Optional的正确用法:
正确创建Optional对象
上面提到创建Optional对象有三个方法,empty()方法比较简单,没什么特别要说明的。主要是of()和ofNullable()方法。当你很确定一个对象不可能为null的时候,应该使用of()方法,否则,尽可能使用ofNullable()方法,比如:
public static void method(Role role) {// 当Optional的值通过常量获得或者通过关键字new初始化,可以直接使用of()方法Optional<String> strOpt = Optional.of("Hello World");Optional<User> userOpt = Optional.of(new User());// 方法参数中role值不确定是否为null,使用ofNullable()方法创建Optional<Role> roleOpt = Optional.ofNullable(role);
}
orElse()方法的使用
return str != null ? str : "Hello World"
上面的代码表示判断字符串str是否为空,不为空就返回,否则,返回一个常量。使用Optional类可以表示为:
return strOpt.orElse("Hello World")
简化if-else
User user = ...
if (user != null) {String userName = user.getUserName();if (userName != null) {return userName.toUpperCase();} else {return null;}
} else {return null;
}
上面的代码可以简化成:
User user = ...
Optional<User> userOpt = Optional.ofNullable(user);
return userOpt.map(User::getUserName).map(String::toUpperCase).orElse(null);
总结一下,新的Optional类让我们可以以函数式编程的方式处理null值,抛弃了Java 8之前需要嵌套大量if-else代码块,使代码可读性有了很大的提高。
Optional的正确打开方式相关推荐
- opengl 贴图坐标控制_材质贴图正确打开方式
哈喽,各位观众朋友们好鸭~欢迎来到讲道理画图的地方,我是黄玮宁. 最近呀经常有小伙伴来问我那些不同通道的材质贴图该怎么用,而且频率不是一般的高,所以我觉得有必要来说说这些通道贴图的用法了. 视频版(B ...
- Optional java 用法_Java8 Optional 的正确使用方式
1.当我们还在以如下几种方式使用 Optional 时, 就得开始检视自己了 调用 isPresent() 方法时 调用 get() 方法时 Optional 类型作为类/实例属性时 Optional ...
- Console控制台的正确打开方式
Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象|-- assert() 如果第一个参数断言为false,则在控制台输出错误信息| ...
- 任务队列和异步接口的正确打开方式(.NET Core版本)
layout: post title: 任务队列和异步接口的正确打开方式(.NET Core版本) category: dotnet core date: 2019-01-12 tags: dotne ...
- log python_基于Python log 的正确打开方式
保存代码到文件:logger.py import os import logbook from logbook.more import ColorizedStderrHandler import sm ...
- python四舍五入round_四舍五入就用round( )?Python四舍五入的正确打开方式!
四舍五入就用round( )?Python四舍五入的正确打开方式! 2018-09-22 21:40 阅读数 4 <>round( )函数简介 菜鸟教程中介绍到,round() 函数作用就 ...
- 通过机器学习识别“迪士尼在逃公主”,程序员宠女的正确打开方式!
到了庆祝的时候了!我们刚刚送走了圣诞老人.现在正等待新年的钟声敲响.所以我想到建立一个很酷的东西(至少我的七岁小公主会觉得)同时学一点机器学习.所以我们要做一个什么? 我借用的我女儿所有迪士尼公主人偶 ...
- pytorch单机多卡的正确打开方式 以及可能会遇到的问题和相应的解决方法
pytorch 单机多卡的正确打开方式 pytorch 使用单机多卡,大体上有两种方式 简单方便的 torch.nn.DataParallel(很 low,但是真的很简单很友好) 使用 torch.d ...
- [分布式训练] 单机多卡的正确打开方式:Horovod
[分布式训练] 单机多卡的正确打开方式:Horovod 转自:https://fyubang.com/2019/07/26/distributed-training4/ 讲完了单机多卡的分布式训练的理 ...
最新文章
- 去分库分表的亿级数据NewSQL实践之旅
- vue使用sweetalert2弹窗插件
- linux 进程通信之 mmap
- C++ 在屏幕上用星号打印菱形
- 快速排序详解+各种实现方式
- 取值方法_「EV3进阶课」制作小游戏:数据取值体系要统一(三)
- 对HashMap的思考及手写实现
- server2012 AD域 radius 802.1x认证
- 怎么把组件挂载到body上_Vue2和Vue3使用上的区别在这,耗子尾汁!
- 关于CUDA中cutil的一些问题
- data fastboot 擦除_fastboot是什么?如何解锁fastboot?
- SAP FICO面试题
- npz文件转为npy_numpy文件存取-npz,npy
- 什么事数据补全_事无补
- 没想到,这款国产软件牛炸了。
- Python数据分析上机
- 正面杠腾讯音乐与网易云音乐,抖音与快手谁能“弯道超车“?
- DS8100更换PPS电源线
- 阿里研究院崔瀚文:“单身经济”背后的“新家园”
- 麻烦攻克食材串味难题!保姆级除味妙招请查收
热门文章
- 下一个7年,保持期待、持续思考,酷雷曼继续向前!
- 求助移动硬盘显示函数不正确
- C#报错:试图加载格式不正确的程序 0x8007000b
- 关于STM32H743的两串口调试纪录笔记
- 休闲船的全球与中国市场2022-2028年:技术、参与者、趋势、市场规模及占有率研究报告
- 2022-2027年中国消防机器人行业市场调研及未来发展趋势预测报告
- HIT oslab之实验2 操作系统的引导(bootsect.s + setup.s)
- 图片横向滚动js代码
- 那个“支付宝锦鲤”女孩,现在变得怎么样了?你肯定想象不到
- 用 Python制作解压缩软件,其实特简单