使用Java8中的Optional类来消除代码中的null检查
简介
Optional类是Java 8新增的一个类,Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)。 —— 每个 Java 程序员都非常了解的异常。
本篇文章将详细介绍optional类,以及如何用它消除代码中的null
检查。
本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。
Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。
但是,Optional 的意义显然不止于此。
避免使用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
对象
1、Optional
类提供了三个方法用于实例化一个Optional
对象,它们分别为empty()
、of()
、ofNullable()
,这三个方法都是静态方法,可以直接调用。
empty()
方法用于创建一个没有值的Optional
对象:
Optional<String> emptyOpt = Optional.empty();
empty()
方法创建的对象没有值,如果对emptyOpt
变量调用isPresent()
方法会返回false
,调用get()
方法抛出NullPointerException
异常。
2、of()
方法使用一个非空的值创建Optional
对象:
String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);
3、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
对象。
如何正确使用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
对象有三个方法,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
代码块,使代码可读性有了很大的提高。
本文参考链接:博客【一书生VOID】https://lw900925.github.io/java/java8-optional.html
使用Java8中的Optional类来消除代码中的null检查的更多相关文章
- Java8系列 (五) Optional类
概述 在Java8之前, 如果需要对一个变量做一次 null 检查, 通常会像下面这样写 T t = service1.query(); if (t != null) { K k = service2 ...
- Java8 新特性 Optional 类
Optional 类的简介 Optional类的是来自谷歌Guava的启发,然后就加入到Java8新特性中去了.Optional类主要就是为子决解价值亿万的错误,空指针异常. Optional ...
- Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句)
Java-Annotation的一种用法(消除代码中冗余的if/else或switch语句) 1.冗余的if/else或switch 有没有朋友写过以下的代码结构,大量的if/esle判断,来选择 ...
- 为什么阿里巴巴Java开发手册中不允许魔法值出现在代码中?
在阅读<阿里巴巴Java开发手册>时,发现有一条关于关于常量定义的规约,具体内容如下: 图中的反例是将数据缓存起来,并使用魔法值加链路 id 组成 key,这就可能会出现其他开发人员在复制 ...
- Java8新特性——Optional类的使用(有效的避免空指针异常)
OPtional类的使用 概述 到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因.以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guav ...
- 【转】消除代码中的 if-else/switch-case
在很多时候,我们代码中会有很多分支,而且分支下面的代码又有一些复杂的逻辑,相信很多人都喜欢用 if-else/switch-case 去实现.做的不好的会直接把实现的代码放在 if-else/swit ...
- 如何将OpenCV中的Mat类绑定为OpenGL中的纹理
https://blog.csdn.net/TTTTzTTTT/article/details/53456324 如果要调用外接的USB摄像头获取图像通常使用OpenCV来调用,如何调用摄像头请参考本 ...
- Swift中如何化简标准库中冗长的类实例初始化代码
可能有些童鞋并不知道,在Swift中缩写点符号对于任何类型的任何static成员都有效. 我们实际写一个例子看一下: import UIKit class CFoo{ static let share ...
- IDEA中的替换功能(替换代码中的变量名很好用哦)
刚刚上班不久,这两天正在研究公司项目里面的代码,今天用阿里的插件扫描了一下代码,发现代码中有很多变量的命名,没有遵循驼峰式的命名规则.一开始我一个一个的修改这些变量名,后来无意中用了一下Ctrl+F( ...
随机推荐
- Springboot 多数据源配置,结合tk-mybatis
一.前言 作为一个资深的CRUD工程师,我们在实际使用springboot开发项目的时候,难免会遇到同时使用多个数据库的情况,比如前脚刚查询mysql,后脚就要查询sqlserver. 这时,我们很直 ...
- IT运维面试问题总结
IT运维面试总结如下,后期更新于:https://www.yuque.com/docs/share/d3dd1e8e-6828-4da7-9e30-6a4f45c6fa8e. 欢迎基于学习.交流目的的 ...
- PHP fileatime() 函数
定义和用法 fileatime() 函数返回指定文件的上次访问时间. 如果成功,该函数将以 Unix 时间戳形式返回文件的上次访问时间.如果失败,则返回 FALSE. 语法 fileatime(fil ...
- PHP cosh() 函数
实例 返回不同数的双曲余弦: <?phpecho(cosh(3) . "<br>");echo(cosh(-3) . "<br>" ...
- AutoWired注解和Lazy延迟加载
一.代码截图: @Lazy是延迟加载的意思, 容器启动时不创建对象, 当从容器中需要获取此对象时才创建. @Lazy//@Lazy注解可以用在类上, 还可以用在普通方法上,还可以用在构造方法上,还可以 ...
- 焦点损失函数 Focal Loss 与 GHM
文章来自公众号[机器学习炼丹术] 1 focal loss的概述 焦点损失函数 Focal Loss(2017年何凯明大佬的论文)被提出用于密集物体检测任务. 当然,在目标检测中,可能待检测物体有10 ...
- 【FZYZOJ】下片 题解(最短路+超级源点)
题目描述 为了提高服务器的耐受能力,很多流量大的网站都会架设多台服务器,而互联网的路由能找到线路最短的一台服务器. 现在UOI想要下片,他有好多台电脑,又有好多服务器可以提供下载.UOI将给你一个网络 ...
- 都2020年了,你还不知道怎么学习Python吗?
众所周知,Python应用广泛,涵盖后端开发.游戏开发.网络爬虫.网站开发.数据挖掘.科学运算.大数据分析.云计算.人工智能等领域,感觉像神一样的存在.Python这么火,那么从入门到精通学习Pyth ...
- Python 告诉你疫情扩散有多可怕
今年(2020年)是注定要铭记史册的一年,从年初开始新冠疫情,席卷了全球,中国人民众志成城,为战胜疫情做出了巨大牺牲.最近北京疫情形式又变得严峻,面对疫情我们不能掉以轻心.今天我们模拟一下病毒的扩散过 ...
- jQuery 购物车案例
h1 { text-align: center; } .cart { width: 1200px; height: 600px; margin: 0 auto; border: 1px solid # ...