百度360必应搜狗淘宝本站头条
当前位置:网站首页 > IT知识 > 正文

Optional 是个好东西,你会用么?| 原力计划

liuian 2025-01-09 14:26 57 浏览

作者 | BoCong-Den

责编 | 夕颜

封图 | CSDN下载自东方IC

出品 | CSDN(ID:CSDNnews)

写在前面

从 Java 8 引入的一个很有趣的特性是 Optional 类。Optional 类主要解决的问题是臭名昭著的空指针异常(PointerException)这个异常就不多说了,肯定是每个 Java 程序员都非常了解的异常。Optional 的完整路径是 java.util.Optional,使用它是为了避免代码中的 if (obj != ) { } 这样范式的代码,可以采用链式编程的风格。而且通过 Optional 中提供的 filter 方法可以判断对象是否符合条件,在符合条件的情况下才会返回,map 方法可以在返回对象前修改对象中的属性。

Optional 的用处

本质上,Optional是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。我们要知道,Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。我们知道,任何访问对象方法或属性的调用都可能导致 PointerException,在这里,我举个简单的例子来说明一下

1String result = test.getName.getTime.getNum.getAnswer;

在上面的这个代码中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查,就是使用if else对test等值进行判断是否为,这很容易就变得冗长,难以维护。为了简化这个过程,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,并鼓励程序员写更干净的代码。Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

Optional 的构造函数

Optional 的三种构造方式:Optional.of(obj), Optional.ofable(obj) 和明确的 Optional.empty

  • Optional.of(obj):它要求传入的 obj 不能是 值的, 否则直接报PointerException 异常。

  • Optional.ofable(obj):它以一种智能的,宽容的方式来构造一个 Optional 实例。来者不拒,传 进到就得到 Optional.empty,非 就调用 Optional.of(obj).

  • Optional.empty:返回一个空的 Optional 对象

Optional 的常用函数

  • of:为非的值创建一个Optional。of方法通过工厂方法创建Optional类。需要注意的是,创建对象时传入的参数不能为。如果传入参数为,则抛出PointerException。因此不经常用。

  • ofable:为指定的值创建一个Optional,如果指定的值为,则返回一个空的Optional。

  • isPresent:如果值存在返回true,否则返回false。

  • ifPresent:如果Optional实例有值则为其调用consumer,否则不做处理

  • get:如果Optional有值则将其返回,否则抛出NoSuchElementException。因此也不经常用。

  • orElse:如果有值则将其返回,否则返回指定的其它值。

  • orElseGet:orElseGet与orElse方法类似,区别在于得到的默认值。orElse方法将传入的字符串作为默认值,orElseGet方法可以接受Supplier接口的实现用来生成默认值

  • orElseThrow:如果有值则将其返回,否则抛出supplier接口创建的异常。

  • filter:如果有值并且满足断言条件返回包含该值的Optional,否则返回空Optional。

  • map:如果有值,则对其执行调用mapping函数得到返回值。如果返回值不为,则创建包含mapping返回值的Optional作为map方法返回值,否则返回空Optional。

  • flatMap:如果有值,为其执行mapping函数返回Optional类型返回值,否则返回空Optional。

Optional 应该怎样用

在使用 Optional 的时候需要考虑一些事情,以决定什么时候怎样使用它。重要的一点是 Optional 不是 Serializable。因此,它不应该用作类的字段。如果你需要序列化的对象包含 Optional 值,Jackson 库支持把 Optional 当作普通对象。也就是说,Jackson 会把空对象看作 ,而有值的对象则把其值看作对应域的值。这个功能在 jackson-modules-java8 项目中。Optional 主要用作返回类型,在获取到这个类型的实例后,如果它有值,你可以取得这个值,否则可以进行一些替代行为。Optional 类可以将其与流或其它返回 Optional 的方法结合,以构建流畅的API。我们来看一个示例,我们不使用Optional写代码是这样的:

1public String getName(User user){
2 if(user == ){
3 return "Unknown";
4 }else return user.name;
5}

接着我们来改造一下上面的代码,使用Optional来改造,我们先来举一个Optional滥用,没有达到流畅的链式API,反而复杂的例子,如下:

1public String getName(User user){
2 Optional<User> u = Optional.ofable(user);
3 if(!u.isPresent){
4 return "Unknown";
5 }else return u.get.name;
6}

这样改写非但不简洁,而且其操作还是和第一段代码一样。无非就是用isPresent方法来替代原先user==。这样的改写并不是Optional正确的用法,我们再来改写一次。

1public String getName(User user){
2 return Optional.ofable(user)
3 .map(u -> u.name)
4 .orElse("Unknown");
5}

这样才是正确使用Optional的姿势。那么按照这种思路,我们可以安心的进行链式调用,而不是一层层判断了。当然,我们还可以通过getter方式,对代码进行进一步缩减(前提是User要有getter方法哦),如下

1String result = Optional.ofable(user)
2 .flatMap(User::getAddress)
3 .flatMap(Address::getCountry)
4 .map(Country::getIsocode)
5 .orElse("default");

Optional 最佳实践

首先我们先上一张图,来简述一下Optional的使用时机:

  • 避免使用Optional.isPresent来检查实例是否存在(上面的举例中提到过),因为这种方式和 != obj没有区别,这样用就没什么意义了。

  • 避免使用Optional.get方式来获取实例对象,因为使用前需要使用Optional.isPresent来检查实例是否存在,否则会出现NoSuchElementException异常问题。所以使用orElse,orElseGet,orElseThrow获得你的结果

这里要说明一下的是orElse(…)是急切计算,意味着类似下面代码:

1Optional<Dog> optionalDog = fetchOptionalDog;
2optionalDog
3 .map(this::printUserAndReturnUser)
4 .orElse(this::printVoidAndReturnUser)

如果值存在则将执行两个方法,如果值不存在,则仅执行最后一个方法。为了处理这些情况,我们可以使用方法orElseGet,它将supplier 作为参数,并且是惰性计算的。

  • 避免使用Optional作为类或者实例的属性,而应该在返回值中用来包装返回实例对象。

  • 避免使用Optional作为方法的参数,原因同3。

  • 不要将赋给Optional

  • 只有每当结果不确定时,使用Optional作为返回类型,从某种意义上讲,这是使用Optional的唯一好地方,用java官方的话讲就是:我们的目的是为库方法的返回类型提供一种有限的机制,其中需要一种明确的方式来表示“无结果”,并且对于这样的方法使用 绝对可能导致错误。

  • 不要害怕使用map和filter,有一些值得遵循的一般开发实践称为SLA-p:Single Layer of Abstraction字母的第一个大写。下面是需要被重构代码到重构的代码


示例一

 1Dog dog = fetchSomeVaraible;
2String dogString = dogToString(dog);
3public String dogToString(Dog dog){
4 if(dog == ){
5 return "DOG'd name is : " + dog.getName;
6 } else {
7 return "CAT";
8 }
9}
10//上面代码重构到下面代码
11Optional<Dog> dog = fetchDogIfExists;
12String dogsName = dog
13 .map(this::convertToDog)
14 .orElseGet(this::convertToCat)
15
16public void convertToDog(Dog dog){
17 return "DOG'd name is : " + dog.getName;
18}
19
20public void convertToCat{
21 return "CAT";
22}

示例二

1Dog dog = fetchDog;
2if(optionalDog != && optionalDog.isBigDog){
3 doBlaBlaBla(optionalDog);
4}
5//上面代码重构到下面代码
6Optional<Dog> optionalDog = fetchOptionalDog;
7optionalDog
8 .filter(Dog::isBigDog)
9 .ifPresent(this::doBlaBlaBla)
  • 不要为了链方法而使用optional。使用optional 时要注意的一件事是链式方法的诱惑。当我们像构建器模式一样链接方法时,事情可能看起来很漂亮。但并不总是等于更具可读性。所以不要这样做,它对性能不利,对可读性也不好。我们应尽可能避免使用引用。

1Dog dog = fetchDog;
2if(optionalDog != && optionalDog.isBigDog){
3 doBlaBlaBla(optionalDog);
4}
5//上面代码重构到下面代码
6Optional<Dog> optionalDog = fetchOptionalDog;
7optionalDog
8 .filter(Dog::isBigDog)
9 .ifPresent(this::doBlaBlaBla)
  • 使所有表达式成为单行lambda。这是更普遍的规则,我认为也应该应用于流。但这篇文章是关于optional 。使用Optional 重要点是记住等式左边和右边一样重要,这里举个例子

 1Optional
2 .ofable(someVariable)
3 .map(variable -> {
4 try{
5 return someREpozitory.findById(variable.getIdOfOtherObject);
6 } catch (IOException e){
7 LOGGER.error(e);
8 throw new RuntimeException(e);
9 }})
10 .filter(variable -> {
11 if(variable.getSomeField1 != ){
12 return true;
13 } else if(variable.getSomeField2 != ){
14 return false;
15 } else {
16 return true;
17 }
18 })
19 .map((variable -> {
20 try{
21 return jsonMapper.toJson(variable);
22 } catch (IOException e){
23 LOGGER.error(e);
24 throw new RuntimeException(e);
25 }}))
26 .map(String::trim)
27 .orElseThrow( -> new RuntimeException("something went horribly wrong."))

上面那么冗长代码块可以使用方法替代:

1Optional
2 .ofable(someVariable)
3 .map(this::findOtherObject)
4 .filter(this::isThisOtherObjectStale)
5 .map(this::convertToJson)
6 .map(String::trim)
7 .orElseThrow( -> new RuntimeException("something went horribly wrong."));

原文链接:

https://blog.csdn.net/DBC_121/article/details/104984093

CSDN VIP会员卡新增权益啦!!!

数百本电子书现在免费阅读啦!下载全站资源,免费观看千门课程。每日学习仅需0.8元。

相关推荐

驱动网卡(怎么从新驱动网卡)
驱动网卡(怎么从新驱动网卡)

网卡一般是指为电脑主机提供有线无线网络功能的适配器。而网卡驱动指的就是电脑连接识别这些网卡型号的桥梁。网卡只有打上了网卡驱动才能正常使用。并不是说所有的网卡一插到电脑上面就能进行数据传输了,他都需要里面芯片组的驱动文件才能支持他进行数据传输...

2026-01-30 00:37 liuian

win10更新助手装系统(微软win10更新助手)

1、点击首页“系统升级”的按钮,给出弹框,告诉用户需要上传IMEI码才能使用升级服务。同时给出同意和取消按钮。华为手机助手2、点击同意,则进入到“系统升级”功能华为手机助手华为手机助手3、在检测界面,...

windows11专业版密钥最新(windows11专业版激活码永久)

 Windows11专业版的正版密钥,我们是对windows的激活所必备的工具。该密钥我们可以通过微软商城或者通过计算机的硬件供应商去购买获得。获得了windows11专业版的正版密钥后,我...

手机删过的软件恢复(手机删除过的软件怎么恢复)
手机删过的软件恢复(手机删除过的软件怎么恢复)

操作步骤:1、首先,我们需要先打开手机。然后在许多图标中找到带有[文件管理]文本的图标,然后单击“文件管理”进入页面。2、进入页面后,我们将在顶部看到一行文本:手机,最新信息,文档,视频,图片,音乐,收藏,最后是我们正在寻找的[更多],单击...

2026-01-29 23:55 liuian

一键ghost手动备份系统步骤(一键ghost 备份)

  步骤1、首先把装有一键GHOST装系统的U盘插在电脑上,然后打开电脑马上按F2或DEL键入BIOS界面,然后就选择BOOT打USDHDD模式选择好,然后按F10键保存,电脑就会马上重启。  步骤...

怎么创建局域网(怎么创建局域网打游戏)

  1、购买路由器一台。进入路由器把dhcp功能打开  2、购买一台交换机。从路由器lan端口拉出一条网线查到交换机的任意一个端口上。  3、两台以上电脑。从交换机任意端口拉出网线插到电脑上(电脑设置...

精灵驱动器官方下载(精灵驱动手机版下载)

是的。驱动精灵是一款集驱动管理和硬件检测于一体的、专业级的驱动管理和维护工具。驱动精灵为用户提供驱动备份、恢复、安装、删除、在线更新等实用功能。1、全新驱动精灵2012引擎,大幅提升硬件和驱动辨识能力...

一键还原系统步骤(一键还原系统有哪些)

1、首先需要下载安装一下Windows一键还原程序,在安装程序窗口中,点击“下一步”,弹出“用户许可协议”窗口,选择“我同意该许可协议的条款”,并点击“下一步”。  2、在弹出的“准备安装”窗口中,可...

电脑加速器哪个好(电脑加速器哪款好)

我认为pp加速器最好用,飞速土豆太懒,急速酷六根本不工作。pp加速器什么网页都加速,太任劳任怨了!以上是个人观点,具体性能请自己试。ps:我家电脑性能很好。迅游加速盒子是可以加速电脑的。因为有过之...

任何u盘都可以做启动盘吗(u盘必须做成启动盘才能装系统吗)

是的,需要注意,U盘的大小要在4G以上,最好是8G以上,因为启动盘里面需要装系统,内存小的话,不能用来安装系统。内存卡或者U盘或者移动硬盘都可以用来做启动盘安装系统。普通的U盘就可以,不过最好U盘...

u盘怎么恢复文件(u盘文件恢复的方法)

开360安全卫士,点击上面的“功能大全”。点击文件恢复然后点击“数据”下的“文件恢复”功能。选择驱动接着选择需要恢复的驱动,选择接入的U盘。点击开始扫描选好就点击中间的“开始扫描”,开始扫描U盘数据。...

系统虚拟内存太低怎么办(系统虚拟内存占用过高什么原因)

1.检查系统虚拟内存使用情况,如果发现有大量的空闲内存,可以尝试释放一些不必要的进程,以释放内存空间。2.如果系统虚拟内存使用率较高,可以尝试增加系统虚拟内存的大小,以便更多的应用程序可以使用更多...

剪贴板权限设置方法(剪贴板访问权限)
剪贴板权限设置方法(剪贴板访问权限)

1、首先打开iphone手机,触碰并按住单词或图像直到显示选择选项。2、其次,然后选取“拷贝”或“剪贴板”。3、勾选需要的“权限”,最后选择开启,即可完成苹果剪贴板权限设置。仅参考1.打开苹果手机设置按钮,点击【通用】。2.点击【键盘】,再...

2026-01-29 21:37 liuian

平板系统重装大师(平板重装win系统)

如果你的平板开不了机,但可以连接上电脑,那就能好办,楼主下载安装个平板刷机王到你的个人电脑上,然后连接你的平板,平板刷机王会自动识别你的平板,平板刷机王上有你平板的我刷机包,楼主点击下载一个,下载完成...

联想官网售后服务网点(联想官网售后服务热线)

联想3c服务中心是联想旗下的官方售后,是基于互联网O2O模式开发的全新服务平台。可以为终端用户提供多品牌手机、电脑以及其他3C类产品的维修、保养和保险服务。根据客户需求层次,联想服务针对个人及家庭客户...