Optional 是个好东西,你会用么?| 原力计划
liuian 2025-01-09 14:26 32 浏览
作者 | 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元。
相关推荐
- 教你把多个视频合并成一个视频的方法
-
一.情况介绍当你有一个m3u8文件和一个目录,目录中有连续的视频片段,这些片段可以连成一段完整的视频。m3u8文件打开后像这样:m3u8文件,可以理解为播放列表,里面是播放视频片段的顺序。视频片段像这...
- 零代码编程:用kimichat合并一个文件夹下的多个文件
-
一个文件夹里面有很多个srt字幕文件,如何借助kimichat来自动批量合并呢?在kimichat对话框中输入提示词:你是一个Python编程专家,完成如下的编程任务:这个文件夹:D:\downloa...
- Java APT_java APT 生成代码
-
JavaAPT(AnnotationProcessingTool)是一种在Java编译阶段处理注解的工具。APT会在编译阶段扫描源代码中的注解,并根据这些注解生成代码、资源文件或其他输出,...
- Unit Runtime:一键运行 AI 生成的代码,或许将成为你的复制 + 粘贴神器
-
在我们构建了UnitMesh架构之后,以及对应的demo之后,便着手于实现UnitMesh架构。于是,我们就继续开始UnitRuntime,以用于直接运行AI生成的代码。PS:...
- 挣脱臃肿的枷锁:为什么说Vert.x是Java开发者手中的一柄利剑?
-
如果你是一名Java开发者,那么你的职业生涯几乎无法避开Spring。它如同一位德高望重的老国王,统治着企业级应用开发的大片疆土。SpringBoot的约定大于配置、SpringCloud的微服务...
- 五年后,谷歌还在全力以赴发展 Kotlin
-
作者|FredericLardinois译者|Sambodhi策划|Tina自2017年谷歌I/O全球开发者大会上,谷歌首次宣布将Kotlin(JetBrains开发的Ja...
- kotlin和java开发哪个好,优缺点对比
-
Kotlin和Java都是常见的编程语言,它们有各自的优缺点。Kotlin的优点:简洁:Kotlin程序相对于Java程序更简洁,可以减少代码量。安全:Kotlin在类型系统和空值安全...
- 移动端架构模式全景解析:从MVC到MVVM,如何选择最佳设计方案?
-
掌握不同架构模式的精髓,是构建可维护、可测试且高效移动应用的关键。在移动应用开发中,选择合适的软件架构模式对项目的可维护性、可测试性和团队协作效率至关重要。随着应用复杂度的增加,一个良好的架构能够帮助...
- 颜值非常高的XShell替代工具Termora,不一样的使用体验!
-
Termora是一款面向开发者和运维人员的跨平台SSH终端与文件管理工具,支持Windows、macOS及Linux系统,通过一体化界面简化远程服务器管理流程。其核心定位是解决多平台环境下远程连接、文...
- 预处理的底层原理和预处理编译运行异常的解决方案
-
若文章对您有帮助,欢迎关注程序员小迷。助您在编程路上越走越好![Mac-10.7.1LionIntel-based]Q:预处理到底干了什么事情?A:预处理,顾名思义,预先做的处理。源代码中...
- 为“架构”再建个模:如何用代码描述软件架构?
-
在架构治理平台ArchGuard中,为了实现对架构的治理,我们需要代码+模型描述所要处理的内容和数据。所以,在ArchGuard中,我们有了代码的模型、依赖的模型、变更的模型等,剩下的两个...
- 深度解析:Google Gemma 3n —— 移动优先的轻量多模态大模型
-
2025年6月,Google正式发布了Gemma3n,这是一款能够在2GB内存环境下运行的轻量级多模态大模型。它延续了Gemma家族的开源基因,同时在架构设计上大幅优化,目标是让...
- 比分网开发技术栈与功能详解_比分网有哪些
-
一、核心功能模块一个基本的比分网通常包含以下模块:首页/总览实时比分看板:滚动展示所有正在进行的比赛,包含比分、比赛时间、红黄牌等关键信息。热门赛事/焦点战:突出显示重要的、关注度高的比赛。赛事导航...
- 设计模式之-生成器_一键生成设计
-
一、【概念定义】——“分步构建复杂对象,隐藏创建细节”生成器模式(BuilderPattern):一种“分步构建型”创建型设计模式,它将一个复杂对象的构建与其表示分离,使得同样的构建过程可以创建...
- 构建第一个 Kotlin Android 应用_kotlin简介
-
第一步:安装AndroidStudio(推荐IDE)AndroidStudio是官方推荐的Android开发集成开发环境(IDE),内置对Kotlin的完整支持。1.下载And...
- 一周热门
-
-
【验证码逆向专栏】vaptcha 手势验证码逆向分析
-
Psutil + Flask + Pyecharts + Bootstrap 开发动态可视化系统监控
-
一个解决支持HTML/CSS/JS网页转PDF(高质量)的终极解决方案
-
再见Swagger UI 国人开源了一款超好用的 API 文档生成框架,真香
-
网页转成pdf文件的经验分享 网页转成pdf文件的经验分享怎么弄
-
C++ std::vector 简介
-
飞牛OS入门安装遇到问题,如何解决?
-
系统C盘清理:微信PC端文件清理,扩大C盘可用空间步骤
-
10款高性能NAS丨双十一必看,轻松搞定虚拟机、Docker、软路由
-
python使用fitz模块提取pdf中的图片
-
- 最近发表
- 标签列表
-
- python判断字典是否为空 (50)
- crontab每周一执行 (48)
- aes和des区别 (43)
- bash脚本和shell脚本的区别 (35)
- canvas库 (33)
- dataframe筛选满足条件的行 (35)
- gitlab日志 (33)
- lua xpcall (36)
- blob转json (33)
- python判断是否在列表中 (34)
- python html转pdf (36)
- 安装指定版本npm (37)
- idea搜索jar包内容 (33)
- css鼠标悬停出现隐藏的文字 (34)
- linux nacos启动命令 (33)
- gitlab 日志 (36)
- adb pull (37)
- python判断元素在不在列表里 (34)
- python 字典删除元素 (34)
- vscode切换git分支 (35)
- python bytes转16进制 (35)
- grep前后几行 (34)
- hashmap转list (35)
- c++ 字符串查找 (35)
- mysql刷新权限 (34)