当前位置: 首页 > news >正文

Optional 详解

Optional 详解

    • 1、Optional 介绍
    • 2、创建 Optional 对象
    • 3、Optional 常用方法
        • 1. 判断值是否存在 — isPresent()
        • 2. 非空表达式 — ifPresent()
        • 3. 设置(获取)默认值 — orElse()、orElseGet()
        • 4. 获取值 — get()
        • 5. 过滤值 — filter()
        • 6. 转换值 — map()

作为一名 Java 程序员,我真的是烦透了 NullPointerException(NPE),尽管和它熟的像一位老朋友,知道它也是迫不得已——程序正在使用一个对象,却发现这个对象的值为 null,于是 Java 虚拟机就怒发冲冠的把它抛出来当作替罪羊。

当然了,我们程序员是富有责任心的,不会坐视不管,于是就有了大量的 null 值检查。尽管有时候这种检查完全没必要,但我们已经习惯了例行公事。终于,Java 8 看不下去了,就引入了 Optional,以便于我们编写的代码不再那么刻薄呆板。
在这里插入图片描述

1、Optional 介绍

  • Optional 类是一个可以为 null 的容器对象。如果值存在则 isPresent() 方法会返回 true,调用 get() 方法会返回该对象。
  • Optional 是一个容器:它可以保存类型 T 的值,或者仅仅保存 null。Optional 提供很多有用的方法,这样我们就不用显示进行空值检测。
  • Optional 类的引用很好的解决了空指针异常。

2、创建 Optional 对象

1)可以使用静态方法 empty()创建一个的 Optional 对象

Optional<Object> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty

2)可以使用静态方法of()创建一个非空的 Optional 对象

Optional<Object> opt = Optional.of("王二");
System.out.println(opt); // 输出:Optional[王二]

当然了,传递给of()方法的参数必须是非空的,也就是说不能为 null ,否则仍然会抛出 NullPointerException。

3)可以使用静态方法ofNullable()创建一个即可空又可非空的 Optional 对象

String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional.empty

ofNullable()方法内部有一个三元表达式,如果参数为 null,则返回私有常量 empty;否则使用 new 关键字创建一个心的 Optional 对象——不会再抛出 NPE 异常了。

3、Optional 常用方法

1. 判断值是否存在 — isPresent()

isPresent()方法可以判断一个 Optional 对象是否存在,如果存在,返回 true,否则返回 false。该方法取代了 obj != null 的判断

Optional<String> opt = Optional.of("王二");
System.out.println(opt.isPresent()); // 输出:trueOptional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false

Java 11 后还可以通过方法isEmpty()(判断值是否为空),判断与isPresent()相反的结果

Optional<String> opt = Optional.of("王二");
System.out.println(opt.isEmpty()); // 输出:falseOptional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isEmpty()); // 输出:true
2. 非空表达式 — ifPresent()

ifPresent()是 Optional 类的一个非常现代化的方法,允许我们使用函数式编程的方法执行一些代码。如果没有该方法的话,我们通常需要先通过isPresent()方法对 Optional 对象进行判空后再执行相应的代码:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {System.out.println(optOrNull.get().length());
}

而有了ifPresent()之后,情况就完全不同了,可以直接讲 Lambda 表达式传递给该方法,代码更加简洁、直观。

Optional<String> opt = Optional.of("王二");
opt.ifPresent(x -> System.out.println(x.length()));

Java 9 后还可以通过方法ifPresentOrElse(action, emptyAction)执行两种结果,非空时执行 action,空时执行 emptyAction。

Optional<String> opt = Optional.of("王二");
opt.ifPresentOrElse(x -> System.out.println(x.length()), () -> System.out.println("为空"));
3. 设置(获取)默认值 — orElse()、orElseGet()

有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse()orElseGet()方法就派上用场了。
orElse()方法用于返回包裹在 Optional 对象中的值,如果该值不为 null ,则返回;否则返回默认值。该方法的参数类型和值的类型一致

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("王二");
System.out.println(name); // 输出:王二

orElseGet()方法与orElse()类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 输出:沉默王二

从输出结果以及代码的形式上来看,这两个方法极其相似,这不免引起我们的怀疑,Java 类库的设计者有必要这做吗?
假设现在有这样一个获取默认值的方法,很传统的方式。

public static String getDefaultValue() {System.out.println("www111");return "w1";}

然后通过orElse()orElseGet()方法分别调用getDefaultValue()返回默认值:

String nullName = null;
String orElse = Optional.ofNullable(nullName).orElse(getDefaultValue());
System.out.println("orElse: "+ orElse);
// 类名::方法名 是 Java 8 引入的语法,方法名后面没有 () 的,表明该方法不一定会被调用
String orElseGet = Optional.ofNullable(nullName).orElseGet(QuesRecordServiceImpl::getDefaultValue);
System.out.println("orElseGet: "+ orElseGet);System.out.println("======================");String name = "w2";
String orElse2 = Optional.ofNullable(name).orElse(getDefaultValue());
System.out.println("orElse2: "+ orElse2);
String orElseGet2 = Optional.ofNullable(name).orElseGet(QuesRecordServiceImpl::getDefaultValue);
System.out.println("orElseGet2: "+ orElseGet2);

结果如下:
在这里插入图片描述
咦,在 Optional 对象的值不为 null 时,orElseGet()没有去调用getDefaultValue()。哪个方法的性能更佳,你明白了吧?

4. 获取值 — get()

直观从语义上来看,get()方法才是最正宗的获取 Optional 对象值的方法,但很遗憾,该方法是有缺陷的,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常。这完全与我们使用 Optional 类的初衷相悖。

public class GetOptionalDemo {public static void main(String[] args) {String name = null;Optional<String> optOrNull = Optional.ofNullable(name);System.out.println(optOrNull.get());}
}

这段程序在运行时会抛出异常:
在这里插入图片描述
尽管抛出的异常是 NoSuchElementException 而不是 NEP,但在我们看来,显然是 “五十步笑百步”。建议使用orElseGet()方法获取 Optional 对象的值。

5. 过滤值 — filter()

filter()方法可以传入一个 Lambda 表达式作为条件,如果表达式结果为 false,则返回一个 empty 的 Optional 对象,否则返回过滤后的 Optional 对象。

String password = "12345";
Optional<String> opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent()); // 输出:false

filter()方法的参数类型为 Predicate(Java 8 新增的一个函数式接口),在上例中,由于 password 所以结果为 false。假设密码长度要求在 6 到 10 位之间,那么还可以再追加一个条件:

Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result); // 输出:true

这次结果为 true。因为密码变成了 7 位,符合条件。想象一下,假如使用 if-else 来完成这个任务,代码该有多冗长。

6. 转换值 — map()

map()方法可以按照一定的规则将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改。

String name = "王二";
Optional<String> nameOptional = Optional.of(name);
Optional<Integer> intOpt = nameOptional.map(String::length);System.out.println( intOpt.orElse(0)); // 输出:2

在上面这个例子中,map()方法的入参String::length,意味着要将原有字符串类型的 Optional 按照字符串长度重新生成一个新的 Optional 对象,类型为 Integer。
当入参也是一个 Optional 时,经过map()转化后会形成一个 Optional<Optional< Integer >> 这种嵌套结构;但flatMap()可以把这种嵌套结构打开:

Optional<Optional<Integer>> unFlatMap = nameOptional.map(x -> Optional.of(x.length()));
Optional<Integer> flatMap = nameOptional.flatMap(x -> Optional.of(x.length()));

好事定律:每件事最后都会是好事,如果不是好事,说明还没到最后。

相关文章:

Optional 详解

Optional 详解 1、Optional 介绍2、创建 Optional 对象3、Optional 常用方法1. 判断值是否存在 — isPresent()2. 非空表达式 — ifPresent()3. 设置(获取)默认值 — orElse()、orElseGet()4. 获取值 — get()5. 过滤值 — filter()6. 转换值 — map() 作为一名 Java 程序员&am…...

(科目三)数据库基础知识

1、基本概念 1.1 数据库 1、数据、信息和数据处理 数据是指表达信息的某种物理符号&#xff1b; 信息是对客观事物的反映&#xff0c;是为某一特定目的二提供的决策数据&#xff1b; 数据处理是指将数据转换成信息的过程&#xff0c;是对各类型的数据进行收集、整理、存储、…...

Unity性能优化篇(十) 模型优化之网格合并 Easy Mesh Combine Tool插件使用以及代码实现网格合并

把多个模型的网格合并为一个网格。可以使用自己写代码&#xff0c;使用Unity自带的CombineMeshes方法&#xff0c;也可以使用资源商店的插件&#xff0c;在资源商店搜Mesh Combine可以搜索到相关的插件&#xff0c;例如Easy Mesh Combine Tool等插件。 可大幅度减少Batches数量…...

0.8秒一张图40hx矿卡stable diffusion webui 高质极速出图组合(24.3.3)

新消息是。经过三个月的等待&#xff0c;SD Webui (automatic1111)终于推出了新版本1.8.0&#xff0c;本次版本最大的更新&#xff0c;可能就是pytorch更新到2.1.2, 不过还是晚了pytorch 2.2.2版。 不过这版的一些更新&#xff0c;在forget分支上早就实现了&#xff0c;所以。…...

手写分布式配置中心(四)增加实时刷新功能(长轮询)

上一篇文章中实现了短轮询&#xff0c;不过短轮询的弊端也很明显&#xff0c;如果请求的频率较高&#xff0c;那么就会导致服务端压力大&#xff08;并发高&#xff09;&#xff1b;如果请求的频率放低&#xff0c;那么客户端感知变更的及时性就会降低。所以我们来看另一种轮询…...

03 | 事务隔离:为什么你改了我还看不见?

提到事务&#xff0c;你肯定不陌生&#xff0c;和数据库打交道的时候&#xff0c;我们总是会用到事务。最经典的例子就是转账&#xff0c;你要给朋友小王转 100 块钱&#xff0c;而此时你的银行卡只有 100 块钱。 转账过程具体到程序里会有一系列的操作&#xff0c;比如查询余…...

Jmeter读取与使用Redis数据

Jmeter 作为当前非常受欢迎的接口测试和性能测试的工具&#xff0c;在企业中得到非常广泛的使用&#xff0c;而 Redis 作为缓存数据库&#xff0c;也在企业中得到普遍使用&#xff0c; Redis服务和客户端安装 windows下安装 默认端口&#xff1a;6379 下载地址&#xff1a; …...

flask 支持跨域访问 非常简单的方式 flask_cors

安装 pip install -U flask-cors from flask import Flask from flask_cors import CORSapp Flask(__name__) CORS(app)app.route("/") def helloWorld():return "Hello, cross-origin-world!"参考 https://www.cnblogs.com/anxminise/p/9814326.html …...

Hololens 2应用开发系列(1)——使用MRTK在Unity中设置混合现实场景并进行程序模拟

Hololens 2应用开发系列&#xff08;1&#xff09;——使用MRTK在Unity中进行程序模拟 一、前言二、创建和设置MR场景三、MRTK输入模拟的开启 一、前言 在前面的文章中&#xff0c;我介绍了Hololens 2开发环境搭建和项目生成部署等相关内容&#xff0c;使我们能生成一个简单Ho…...

Newtonsoft.Json

目录 引言 1、简单使用 1.1、官方案例 1.2、JsonConvert 2、特性 2.1、默认模式[JsonObject(MemberSerialization.OptIn/OptOut)] 2.2、序列化为集合JsonArrayAttribute/JsonDictionaryAttribute 2.3、序列化该元素JsonProperty 2.4、忽略元素JsonIgnoreAttribute 2.5、…...

速卖通平台的API返回结果有哪些数据字段?

速卖通&#xff08;AliExpress&#xff09;作为阿里巴巴旗下的国际电商平台&#xff0c;提供了API接口供开发者使用&#xff0c;以获取商品、订单、物流等各种信息。然而&#xff0c;速卖通API返回的具体数据字段可能会随着API版本、接口类型以及时间的变化而有所不同。 在编写…...

C++ 标准模板库(STL)

1、vector 动态数组&#xff0c;可随时添加删除元素&#xff0c;在堆空间开辟内存。 方法含义front() 返回第一个元素O(1) back()返回最后一个元素O(1)pop_back()删除最后一个元素O(1)push_back(ele)在末尾插入元素O(1)size()返回实际元素个数O(1)clear()清除所有元素O(N)resi…...

【Javascript】设计模式之发布订阅模式

文章目录 1、现实中的发布&#xff0d;订阅模式2、DOM 事件3、简单的发布-订阅模式4、通用的发布-订阅模式5、先发布再订阅6、小结 发布—订阅模式又叫观察者模式&#xff0c;它定义对象间的一种一对多的依赖关系&#xff0c;当一个对象的状态发生改变时&#xff0c;所有依赖于…...

DataLoader

import torchvision from torch.utils.data import DataLoader from torch.utils.tensorboard import SummaryWriter# 准备的测试数据集 数据放在了CIFAR10文件夹下test_data torchvision.datasets.CIFAR10("./CIFAR10",trainFalse, transformtorchvision.transfor…...

持续集成(CICD)- Jenkins+Git+gogs综合实战(笔记二)

文章目录 七、拉取代码方法一:直接填写命令进行拉取(不建议用这种)方法二:使用源码管理拉取代码步骤一:确认环境(检查自己是否有Git插件)步骤二:构建项目时对项目的源码管理选择 Git步骤三:输入你仓库的SSH地址或者https地址,并且添加gitee的用户名和密方法一和方法二…...

VUE:key属性的作用

在 Vue.js 中&#xff0c;key属性的主要作用是帮助 Vue 在进行 DOM 更新时&#xff0c;能够更准确地识别哪些节点可以复用。 当key值发生变化时&#xff0c;Vue 会执行以下步骤&#xff1a; 1.查找旧节点&#xff1a;Vue 会查找虚拟 DOM 中具有旧key值的节点。 2.匹配新节点…...

linux的通信方案(SYSTEM V)

文章目录 共享内存(Share Memory)信号队列&#xff08;Message Queue&#xff09;信号量(semaphore) 进程间通信的核心理念&#xff1a;让不同的进程看见同一块资源 linux下的通信方案&#xff1a; SYSTEM V 共享内存(Share Memory) 特点&#xff1a;1.共享内存是进程见通信最…...

VUE 入门及应用 ( 路由 router )

6.前端路由 router Vue Router | Vue.js 的官方路由 (vuejs.org) 官方地址 : https://router.vuejs.org/zh/ 6.1.基本配置 6.1.0.准备 MyPage.vue 创建 用于测试 vue文件 ../views/MyPage.vue <template><div><h1>MyPage</h1></div> </…...

SpringBoot集成RocketMQ

RocketMQ是一个纯Java、分布式、队列模型的开源消息中间件&#xff0c;前身是MetaQ&#xff0c;是阿里参考Kafka特点研发的一个队列模型的消息中间件&#xff0c;后开源给apache基金会成为了apache的顶级开源项目&#xff0c;具有高性能、高可靠、高实时、分布式特点。 环境搭…...

【Web】关于FastJson反序列化开始前的那些前置知识

目录 FastJson介绍 FJ序列化与反序列化方法 关于反序列化三种方式的关系与区别 FastJson反序列化漏洞原理通识 关于getter&setter FastJson介绍 FastJson&#xff08;快速JSON&#xff09;是一个Java语言编写的高性能、功能丰富且易于使用的JSON解析和序列化库。它由…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?

大家好&#xff0c;欢迎来到《云原生核心技术》系列的第七篇&#xff01; 在上一篇&#xff0c;我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在&#xff0c;我们就像一个拥有了一块崭新数字土地的农场主&#xff0c;是时…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八

现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet&#xff0c;点击确认后如下提示 最终上报fail 解决方法 内核升级导致&#xff0c;需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...

探索Selenium:自动化测试的神奇钥匙

目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...