Java8 判空新写法(Optional方式)

news/2024/4/23 18:09:47/

1 引言

在文章的开头,先说下NPE问题,NPE问题就是,我们在开发中经常碰到的NullPointerException.假设我们有两个类,他们的UML类图如下图所示
在这里插入图片描述
在这种情况下,有如下代码

user.getAddress().getProvince();

这种写法,在user为null时,是有可能报NullPointerException异常的。为了解决这个问题,于是采用下面的写法

if(user!=null){Address address = user.getAddress();if(address!=null){String province = address.getProvince();}
}

这种写法是比较丑陋的,为了避免上述丑陋的写法,让丑陋的设计变得优雅。JAVA8提供了Optional类来优化这种写法,接下来的正文部分进行详细说明

2 API介绍

先介绍一下API,与其他文章不同的是,本文采取类比的方式来讲,同时结合源码。而不像其他文章一样,一个个API罗列出来,让人找不到重点。

2.1 Optional(T value),empty(),of(T value),ofNullable(T value)

这四个函数之间具有相关性,因此放在一组进行记忆。

先说明一下,Optional(T value),即构造函数,它是private权限的,不能由外部调用的。其余三个函数是public权限,供我们所调用。那么,Optional的本质,就是内部储存了一个真实的值,在构造的时候,就直接判断其值是否为空。好吧,这么说还是比较抽象。直接上Optional(T value)构造函数的源码,如下图所示
在这里插入图片描述
那么,of(T value)的源码如下

public static <T> Optional<T> of(T value) {return new Optional<>(value);
}

也就是说of(T value)函数内部调用了构造函数。根据构造函数的源码我们可以得出两个结论:

  • 通过of(T value)函数所构造出的Optional对象,当Value值为空时,依然会报NullPointerException。
  • 通过of(T value)函数所构造出的Optional对象,当Value值不为空时,能正常构造Optional对象。

除此之外呢,Optional类内部还维护一个value为null的对象,大概就是长下面这样的

public final class Optional<T> {//省略....private static final Optional<?> EMPTY = new Optional<>();private Optional() {this.value = null;}//省略...public static<T> Optional<T> empty() {@SuppressWarnings("unchecked")Optional<T> t = (Optional<T>) EMPTY;return t;}
}

那么,empty()的作用就是返回EMPTY对象。

好了铺垫了这么多,可以说ofNullable(T value)的作用了,上源码

 public static <T> Optional<T> ofNullable(T value) {return value == null ? empty() : of(value);
}

好吧,大家应该都看得懂什么意思了。相比较of(T value)的区别就是,当value值为null时,of(T value)会报NullPointerException异常;ofNullable(T value)不会throw Exception,ofNullable(T value)直接返回一个EMPTY对象。

那是不是意味着,我们在项目中只用ofNullable函数而不用of函数呢?

不是的,一个东西存在那么自然有存在的价值。当我们在运行过程中,不想隐藏NullPointerException。而是要立即报告,这种情况下就用Of函数。但是不得不承认,这样的场景真的很少。博主也仅在写junit测试用例中用到过此函数。

2.2 orElse(T other),orElseGet(Supplier<? extends T> other)和orElseThrow(Supplier<? extends X> exceptionSupplier)

这三个函数放一组进行记忆,都是在构造函数传入的value值为null时,进行调用的。orElse和orElseGet的用法如下所示,相当于value值为null时,给予一个默认值:

@Test
public void test() {User user = null;user = Optional.ofNullable(user).orElse(createUser());user = Optional.ofNullable(user).orElseGet(() -> createUser());}
public User createUser(){User user = new User();user.setName("zhangsan");return user;
}

这两个函数的区别:当user值不为null时,orElse函数依然会执行createUser()方法,而orElseGet函数并不会执行createUser()方法,大家可自行测试。

至于orElseThrow,就是value值为null时,直接抛一个异常出去,用法如下所示

User user = null;
Optional.ofNullable(user).orElseThrow(()->new Exception("用户不存在"));

2.3 map(Function<? super T, ? extends U> mapper)和flatMap(Function<? super T, Optional> mapper)

这两个函数放在一组记忆,这两个函数做的是转换值的操作。

直接上源码

 public final class Optional<T> {//省略....public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {Objects.requireNonNull(mapper);if (!isPresent())return empty();else {return Optional.ofNullable(mapper.apply(value));}}//省略...public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {Objects.requireNonNull(mapper);if (!isPresent())return empty();else {return Objects.requireNonNull(mapper.apply(value));}}
}

这两个函数,在函数体上没什么区别。唯一区别的就是入参,map函数所接受的入参类型为Function<? super T, ? extends U>,而flapMap的入参类型为Function<? super T, Optional>。

在具体用法上,对于map而言:

如果User结构是下面这样的

public class User {private String name;public String getName() {return name;}
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).map(u-> u.getName()).get();

对于flatMap而言:

如果User结构是下面这样的

public class User {private String name;public Optional<String> getName() {return Optional.ofNullable(name);}
}

这时候取name的写法如下所示

String city = Optional.ofNullable(user).flatMap(u-> u.getName()).get();

4.1 isPresent()和ifPresent(Consumer<? super T> consumer)

这两个函数放在一起记忆,isPresent即判断value值是否为空,而ifPresent就是在value值不为空时,做一些操作。这两个函数的源码如下

public final class Optional<T> {//省略....public boolean isPresent() {return value != null;}//省略...public void ifPresent(Consumer<? super T> consumer) {if (value != null)consumer.accept(value);}
}

需要额外说明的是,大家千万不要把

if (user != null){// TODO: do something
}

给写成

User user = Optional.ofNullable(user);
if (Optional.isPresent()){// TODO: do something
}

因为这样写,代码结构依然丑陋。博主会在后面给出正确写法

至于ifPresent(Consumer<? super T> consumer),用法也很简单,如下所示

Optional.ofNullable(user).ifPresent(u->{// TODO: do something
});

2.5 filter(Predicate<? super T> predicate)

不多说,直接上源码

public final class Optional<T> {//省略....Objects.requireNonNull(predicate);if (!isPresent())return this;elsereturn predicate.test(value) ? this : empty();
}

filter 方法接受一个 Predicate 来对 Optional 中包含的值进行过滤,如果包含的值满足条件,那么还是返回这个 Optional;否则返回 Optional.empty。

用法如下

Optional<User> user1 = Optional.ofNullable(user).filter(u -> u.getName().length()<6);

如上所示,如果user的name的长度是小于6的,则返回。如果是大于6的,则返回一个EMPTY对象。

3 实战使用

3.1 例一

在函数方法中

以前写法

public String getCity(User user)  throws Exception{if(user!=null){if(user.getAddress()!=null){Address address = user.getAddress();if(address.getCity()!=null){return address.getCity();}}}throw new Excpetion("取值错误"); }

JAVA8写法

public String getCity(User user) throws Exception{return Optional.ofNullable(user).map(u-> u.getAddress()).map(a->a.getCity()).orElseThrow(()->new Exception("取指错误"));
}

3.2 例二

在主程序中

以前写法

if(user!=null){dosomething(user);
}

JAVA8写法

 Optional.ofNullable(user).ifPresent(u->{dosomething(u);
});

3.3 例三

以前写法

public User getUser(User user) throws Exception{if(user!=null){String name = user.getName();if("zhangsan".equals(name)){return user;}}else{user = new User();user.setName("zhangsan");return user;}
}

java8写法

public User getUser(User user) {return Optional.ofNullable(user).filter(u->"zhangsan".equals(u.getName())).orElseGet(()-> {User user1 = new User();user1.setName("zhangsan");return user1;});
}

其他的例子,不一一列举了。不过采用这种链式编程,虽然代码优雅了。但是,逻辑性没那么明显,可读性有所降低,大家项目中看情况酌情使用。


http://www.ppmy.cn/news/46798.html

相关文章

Lumen6 /laravel 框架路由请求实现token验证

版本 Lumen6.0 中文文档&#xff1a;https://learnku.com/docs/lumen/5.7/cache/2411 实现功能效果 1、使用缓存存储用户token 2、从请求头head 中获取用户token 3、返回指定的认证失败结构体 4、对指定的接口路由做身份验证 第一步&#xff1a;解除注释 注意&#xff1…

Java的7大好处

Java 的 7 大优势 当 Sun Microsystems 在 1995 年发布 Java 语言环境白皮书时&#xff0c;他们列出了 Java 将给软件开发领域带来的以下 7 大好处&#xff1a; Java 简单而熟悉。Java是面向对象的。Java 是体系结构中立的。Java 健壮且安全。Java是多线程的。Java具有高性能。…

哈希表题目:在系统中查找重复文件

文章目录 题目标题和出处难度题目描述要求示例数据范围进阶 解法思路和算法代码复杂度分析 进阶问题答案后记 题目 标题和出处 标题&#xff1a;在系统中查找重复文件 出处&#xff1a;609. 在系统中查找重复文件 难度 6 级 题目描述 要求 给定一个目录信息列表 paths…

入门神经网络——浅层神经网络

文章目录 一、基础知识1.浅层神经网络介绍2.浅层神经网络的正向传播3.反向传播 二、浅层神经网络代码实例 一、基础知识 1.浅层神经网络介绍 此次构件浅层神经网络&#xff0c;相比于单神经元&#xff0c;浅层神经网络拥有多个神经元&#xff0c;因此又可以称为多神经元网络&…

Ubuntu 自带截图工具快捷键盘

PrtSc – 获取整个屏幕的截图并保存到 Pictures 目录。 Shift PrtSc – 获取屏幕的某个区域截图并保存到 Pictures 目录。 Alt PrtSc –获取当前窗口的截图并保存到 Pictures 目录。 Ctrl PrtSc – 获取整个屏幕的截图并存放到剪贴板。 Shift Ctrl PrtSc – 获取屏幕的某个…

【消费战略】解读100个食品品牌丨王小卤 4年10亿爆品破局

爆品破局 王小卤的聚焦发展! 王小卤创建于 2016 年&#xff0c;与饮料行业的独角兽元气森林同年。 相较于元气森林的快速增长&#xff0c;王小卤历经 三年坎坷之路&#xff0c;直至 2019 年才踏上高增长的赛道&#xff0c;实现四年十亿的增长。 “所有的消费品都值得重新 做…

SSM框架MyBatis 三种分页查询 PageHlper的使用以及五个参数的简单解释

SSM框架MyBatis 三种简单的分页查询 1. 基础分页查询&#xff08;环境在第一天的配置中有&#xff09; mapper也就是dao //查询总数Select("select count(*) from book;")int selectCount();//分页查询Select("select * from book limit #{currpage},#{size}&q…

KD2684S电机匝间耐电压测试仪

一、产品简介 试验仪适用于电机、变压器、电器线圈等这些由漆包线绕制的产品。因漆包线的绝缘涂敷层本身存在着质量问题&#xff0c;以及在绕线、嵌线、刮线、接头端部整形、绝缘浸漆、装配等工序工艺中不慎而引起绝缘层的损伤等&#xff0c;都会造成线圈层间或匝间绝缘层的绝缘…

NIFI从MySql中离线读取数据再导入到MySql中_03_来吧用NIFI实现_数据分页获取功能---大数据之Nifi工作笔记0038

之前使用querydatabasetable处理器来获取mysql中的数据,我们只能写死一个sql的查询语句,但是 实际引用环境中,我们的一张mysql的表,可能有上千万的数据,那么,不可能,我们把sql查询语句写死,这样一次性如果获取所有数据,那么压力太大了,我们怎么弄呢?找了很久没有找到相关教程…

Java 8 中使用 Lambda 表达式和 Stream API 解决 LeetCode 的两数之和问题

Java 8 中使用 Lambda 表达式和 Stream API 解决 LeetCode 的两数之和问题 当我们在面对一个数列&#xff0c;需要查找其中两个元素的和为给定目标值时&#xff0c;可以使用两数之和&#xff08;Two Sum&#xff09;问题来解决。这个问题在 LeetCode 上有很高的重要性和普遍性&…

机器人提示词工程师 Robotics Prompt Engineer

还没毕业&#xff0c;在校学习的各项技能都已经没用了&#xff0c;也别急着焦虑和忧伤&#xff0c;工业时代到信息时代&#xff0c;信息时代到智能时代&#xff0c;换代对每个普通人都是非常具有挑战性的&#xff0c;也是新一轮洗牌的开始。 机器人提示词工程师的核心竞争力包括…

3.3 二维随机变量条件分布

学习目标&#xff1a; 要学习二维随机变量的条件分布&#xff0c;我可能会采取以下步骤&#xff1a; 复习边缘分布和联合分布&#xff1a;首先需要了解二维随机变量的边缘分布和联合分布的概念以及相应的公式。 复习条件概率&#xff1a;学习条件概率的定义和计算公式&#x…

nodegui搭建/你好/打包

0、github连接问题 警告&#xff1a;如果你的网络有任何有任何有任何有任何有任何有任何有任何有任何有任何有任何连接 github 的问题&#xff0c;彻底放弃该框架 请转到其他框架 electron-egg教程、electron-egg官网&#xff0c;或其他electron项目 Tauri教程、Tauri官网 NW.…

【python零碎】

1. 拼接字符中&#xff0c;插入变量 >>> shepherd "Mary" >>> age 32 >>> stuff_in_string "Shepherd {} is {} years old.".format(shepherd, age) >>> print(stuff_in_string) Shepherd Mary is 32 years old. &…

2.3 连续性随机变量

思维导图&#xff1a; 学习目标&#xff1a; 我会按照以下步骤学习连续型随机变量&#xff1a; 复习概率论的基础知识&#xff0c;包括概率、期望、方差等概念和公式&#xff0c;以及离散型随机变量的概率分布函数和概率质量函数的概念和性质。 学习连续型随机变量的概念和性…

【Android安全】Soot 静态分析教程

参考教程 https://github.com/noidsirius/SootTutorial Windows Soot 环境配置 下载代码 git 拷贝仓库 git init git clone https://github.com/noidsirius/SootTutorial.git ./gradlew.bat build 报错&#xff1a;Unsupported class file major version 57 ./gradlew.b…

编译预处理:#if

用法 #if <expression> … #elif … #end <expression> 是整数常量比较的表达式&#xff0c;例如&#xff1a; defined表达式&#xff0c;例如 defined AAA, 或者 defined(AAA), 如果AAA是一个宏定义&#xff0c;return true&#xff0c;否则&#xff0c;return…

2023年全国最新安全员精选真题及答案55

百分百题库提供安全员考试试题、建筑安全员考试预测题、建筑安全员ABC考试真题、安全员证考试题库等&#xff0c;提供在线做题刷题&#xff0c;在线模拟考试&#xff0c;助你考试轻松过关。 81.&#xff08;单选题&#xff09;扣件式钢管模板支架的剪刀撑应用旋转扣件进行固定&…

HTML—javaEE

文章目录 1.认识HTML2.HTML标签的使用2.1注释2.2标题2.3段落2.4换行2.5字体加粗、斜体字、删除线、下划线2.6图片2.7超链接2.8表格2.9列表2.10表单标签2.11div2.12span 3.HTML特殊符号 1.认识HTML &#xff08;1&#xff09;HTML是网页的编程语言&#xff0c;文件的内容主要由…

lamp 架构的搭建

php 解释动态页面 php来连接数据库 mysql 页面信息和端口信息 存放数据 apache 前端web服务器&#xff0c;展现页面 源码编译安装这三个服务 配置下载apache: systemctl stop firewalld 关闭安全机制&#xff0c;防火墙 可以一条命令:systemctl is-enabled firewalld 和 s…