合理利用Optional 来避免NPE

news2025/4/6 0:18:23

一、什么是Optional

在Java中什么异常最容易出现,那肯定是NullPointerException,空指针就像一个定时炸弹,总给我们带来些麻烦,在开发过程中都会碰到需要判断Null值以防止空指针的情况,以往的方式要么是抛异常,要么是if{}else{},直到Optional的出现,你可以更优雅的解决NPE问题。
首先我们来看一下 Optional的作者 Brian Goetz 对这个 API 的说明:

Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent “no result”, and using null for such was overwhelmingly likely to cause errors.

翻译过来大致意思:Optional提供了一个有限的机制让类库方法返回值清晰的表达有与没有值,避免很多时候 null 造成的错误
所以尝试在Java8+中使用工具类Optional,并在特定场景中简化代码逻辑。

二、Optional中有哪些常用方法

静态方法

  • Optional.of(T value):为指定的值创建一个指定非 null 值的 Optional,需要注意的是传入的参数不能为 null,否则抛出 NullPointerException。
  • Optional.ofNullable():为指定的值创建一个 Optional 对象,如果指定的参数为 null,不抛出异常,直接则返回一个空的 Optional 对象。

对象方法

  • isPresent():判断optional是否为空,如果空则返回false,否则返回true
  • get():如果 Optional 有值则将其返回,否则抛出 NoSuchElementException 异常
  • ifPresent(Consumer c):如果optional不为空,则将optional中的对象传给Comsumer函数orElse(T other):如果optional不为空,则返回optional中的对象;如果为null,则返回 other 这个默认值
  • orElseGet(Supplier other):如果optional不为空,则返回optional中的对象;如果为null,则使用Supplier函数生成默认值other
  • orElseThrow(Supplier exception):如果optional不为空,则返回optional中的对象;如果为null,则抛出Supplier函数生成的异常
  • filter(Predicate p):如果optional不为空,则执行断言函数p,如果p的结果为true,则返回原本的optional,否则返回空的optional
  • map(Function<T, U> mapper):如果optional不为空,则将optional中的对象 t 映射成另外一个对象 u,并将 u 存放到一个新的optional容器中。
  • flatMap(Function< T,Optional< U >> mapper):跟上面一样,在optional不为空的情况下,将对象t映射成另外一个optional,区别:map会自动将u放到optional中,而flatMap则需要手动给u创建一个optional

三、Optional误用场景

1. 直接使用 isPresent() 进行 if 检查

这么写和直接判断空没啥区别,反而增加了麻烦

public class User{
	private String name;
	private Integer age;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}
// 错误使用Optional
	User u=null;
	Optional<User> userOpt=Optional.ofNullable(u);
	if(userOpt.isPresent()){
		String name=u.getName();
		System.out.println(name);
	}
	// 直接判断
	if(u !=null){
		String name=u.getName();
		System.out.println(name);
	}

可以接受的写法:建议使用 ifPresent(Consumer<? super T> consumer),判断Optional中是否有值,有值则执行 consumer,否则什么都不干,不建议直接使用isPresent() 进行 if 检查进行判断

userOpt.ifPresent(user -> System.out.println(user.getName()));

其实isPresent() 更建议用在流处理的结尾,用于判断是否符合条件

list.stream()
    .filer(x -> Objects.equals(x,param))
    .findFirst()
    .isPresent()

2. 在方法参数中使用 Optional或者在POJO中使用

Optional本身并没有实现序列化,现有的 JSON 序列化框架大多数也没有对此提供支持的。

public class User {
    private String name;
	private Integer age;
    // 不建议
    private Optional<String> address;
}

3. 直接使用 Optional.get

Optional不会帮你做任何的空判断或者异常处理,如果直接在代码中使用 Optional.get()和不做任何空判断一样,十分危险,当没有值时会抛出一个NoSuchElementException异常

User u=null;
Optional<User> userOpt=Optional.ofNullable(u);
// 不建议
System.out.println(userOpt.get().getName());

得到得结果是:
在这里插入图片描述
获取 value 有三种方式:get() 、orElse()、 orElseGet()
get()不能直接使用,需要结合判空使用。这和!=null其实没多大区别,只是在表达和抽象上有所改善。
orElse() 和ElseGet() 区别
性能问题:无论如何都会执行括号中的内容, orElseGet()只在主体 value 是空时执行orElseGet()需要构建一个Supplier
使用不当,再次空指针,当orElse的参数是间接计算得来的时候。虽然这种说法有点牵强(因为并不是orElse导致了空指针异常),但是使用orElseGet确实可以避免这种情况

class User {
    // 中文名
	private String chineseName;
	// 英文名
	private EnglishName englishName;
}

class EnglishName {
    // 全名
    private String fullName;
    // 简写
    private String shortName;
}

假如我们现在有User类,用户注册账号时,需要提供自己的中文名或英文名,或都提供,我们抽象出一个EnglishName类,它包含英文名的全名和简写(因为有的英文名确实太长了)。现在,我们希望有一个User#getName()方法,它可以像下面这样实现:当用户只提供了中文名时,此时englishName属性是null,但是在orElse中,englishName.getShortName()总是会执行。而在getName2()中,这个风险却没有。

class User {
    // ... 之前的内容
    public String getName1() {
        return Optional.ofNullable(chineseName)
                .orElse(englishName.getShortName());
    }
    public String getName2() {
        return Optional.ofNullable(chineseName)
                .orElseGet(() -> englishName.getShortName());
    }
}

如果只是简单的返回一个静态资源、字符串等等,直接返回静态资源使用orElse()即可。

4. 使用在注入的属性中

// 一般不建议
public class CommonService {
    private Optional<UserService> userService;
}

四、Optional正确使用姿势

1.将对象某个属性赋值给其他实体对象

// 调用服务层查询数据库获得目标对象
ProdSku prodSku = prodSKUService.getFirstProdSku(p.getProdId());
if (prodSku != null) {
    prodAPP.setPrice(prodSku.getPrice());
}
// 使用Optional 和函数式编程,一次完成
Optional.ofNullable(prodSku).ifPresent(p -> prodAPP.setPrice(p.getPrice()));

2.当取不到值时,返回你指定的 default

如果指定值固定的,明确的建议用orElse(),如果指定值是不明确的或者是需要大量计算得出的建议用orElseGet()

2.1判断一个集合大小,但又怕这个集合是null

List<User> list=null;
Optional<List<User>> optional=Optional.ofNullable(list);
int size = optional.map(List::size).orElse(0);
// 输出0
System.out.println(size);
// 直接调用集合的size方法就有可能造成 NullPointerException 空指针异常
System.out.println(list.size());

2.2实体类get方法初始化赋值

// 当传给前端同学一个字段时候,并不希望这个值出现null,但数据库里确实就是null的情况
public BigDecimal getAccount() {
        if (account == null) {
            account = BigDecimal.ZERO;
        }
        return account;
}
// 代替:
public BigDecimal getAccount() {
        return Optional.ofNullable(account).orElse(BigDecimal.ZERO) ;
}

2.3 类型之间转换,当没有值时候就返回一个默认值

String price=null;
Double c=Optional.ofNullable(price).map(a->Double.valueOf(a)).orElse(0.00);
System.out.println(c);
// 直接转换必定空指针错误
Double b=Double.valueOf(price);

2.4获取对象对应的汉字信息

如果是null就直接返回不获取,不论是a是null,还是对a运算是null,都能走到orElse 里面去的

Optional<Integer> optional=Optional.ofNullable(super.getAduitStatus());
        return optional.map(a->AduitStatusEnum.name(String.valueOf(a))).orElse(null);

3.多层属性获取(获取一个多层嵌套实体的值,收益最明显的一个例子)

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是不是null++++++++++++++++++++++++++++++++++++
    InetSocketAddress inetSocketAddress = new InetSocketAddress(2);
    String re="小明取不到值";
    if(inetSocketAddress !=null){
        if(inetSocketAddress.getAddress() !=null){
            if(inetSocketAddress.getAddress().getHostName() !=null){
                String name2 = inetSocketAddress.getAddress().getHostName().toUpperCase();
                if(StringUtils.isNotBlank(name2) ){
                    re=name2;
                }
            }
        }
    }
    System.out.println(re);

    Optional<InetSocketAddress> optional=Optional.ofNullable(inetSocketAddress);
    String op= optional.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op);

 //+++++++++++++++++++++++模拟这个要取的对象从其他接口传过来是null++++++++++++++++++++++++++++++++++++
    String re2="小明取不到值";
    InetSocketAddress inetSocketAddress2 = null;
    if(inetSocketAddress2 !=null){
        if(inetSocketAddress2.getAddress() !=null){
            if(inetSocketAddress2.getAddress().getHostName() !=null){
                String name2 = inetSocketAddress2.getAddress().getHostName().toUpperCase();
                if(name2 ==null){
                    re2=name2;
                }
            }
        }
    }
    System.out.println(re2);

    Optional<InetSocketAddress> optional2=Optional.ofNullable(inetSocketAddress2);
    String op2= optional2.map(a ->a.getAddress()).map(b ->b.getHostName()).map(c->c.toUpperCase()).orElse("小王也取不到值");
    System.out.println(op2);

运行结果如下图所示
在这里插入图片描述

4.阻塞性业务场景推荐使用

BigDecimal stock = Optional.ofNullable(lcStockObj).map(a -> a.getAssetNum())
                           .orElseThrow(() -> new RuntimeException("库存数据不存在,出入库失败"));

参考链接:https://blog.csdn.net/rhythm_1/article/details/121716010

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/541171.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(3)---STM32通信

目录 【1】通信的基础知识 【2】USART 【3】串口通信协议 【4】相关寄存器 串口控制寄存器 波特率寄存器 中断和状态寄存器 数据发送寄存器 数据接收寄存器 【5】 USART功能框图 【6】串口发送实验 实验要求 1.观察实物 2.分析原理图 3.STM32CubeMX配置 4、寄存器方式编写…

选择性搜索算法(Selective Search )——SS算法

文章目录 一、前言二、object Detection VS object Recognition&#xff08;Selective Search的提出&#xff09;2.1object recognition与object detection的关系2.2滑动窗口方法的局限性2.3Selective search算法的提出 三、Selective Search算法3.1什么是Selective Search&…

ChatGPT工作提效之遇强则强

ChatGPT工作提效之遇强则强 前言一、如何使用ChatGPT二、ChatGPT实战应用三、ChatGPT会叫的小孩有奶吃工具类的交互问答类的交互开发类的交互 前言 读《笑傲江湖》西湖比剑时&#xff0c;对于独孤九剑1的解读印象颇为深刻。令狐冲被任我行这个高手激发出许多精妙的剑招。这独孤…

消息队列-RabbitMQ

文章目录 1.什么是MQ1.1 特点1.2 MQ产品分类 2.RabbitMQ2.1.RabbitMQ介绍2.2.使用Docker安装RabbitMQ 3.SpringBoot中使用RabbitMQ3.1.SpringAMQP3.2使用步骤 1.什么是MQ RabbitMQ官方文档 消息队列(Message Queue&#xff0c;简称MQ)&#xff1a;是在消息的传输过程中保存消…

Learning C++ No.22【二叉树OJ题实战】

文章目录 引言&#xff1a;第一题&#xff1a;根据二叉树创建字符串第二题&#xff1a;二叉树的层序遍历第三题&#xff1a;自底向上实现层序遍历第四题&#xff1a;二叉树的最近公共祖先第五题&#xff1a;将搜索二叉树转换成双向链表第六题&#xff1a;从前序和中序遍历序列构…

修改USRPx410的ip地址

用 .\uhd_find_devices.exe查询设备 打印信息解释如下 在C:\Program Files\UHD\bin下打开powershell,输入如下指令ssh root@192.168.10.2 进入到设备内部 输入ifconfig,获取每个口的地址 输入ifconfig sfp0 192.168.10.3进行修改 重新.\uhd_find_devices.exe查询设备。 …

Linux:软件安装方法与命令yum.扩展ubantu系统的软件安装方法与命令apt

Linux&#xff1a;软件安装方法与命令yum: 这里的search可以想象成在linux的应用商店内搜索 例&#xff1a; 通过yum安装wget这个软件。 search搜索wget&#xff1a; 发现在“linux的应用市场”内有wget这个软件&#xff0c;现在再用yum -y install wget安装&#xff08;上面已…

你好:Zookeeper

Zookeeper 初识 初识ZookeeperZookeeper 安装Zookeeper 命令操作Zookeeper数据模型服务端命令客户端命令JavaAPI操作Curator常用操作&#xff1a;前置环境连接Zookeeper集群创建节点查询节点修改节点删除节点事件监听机制 – Watch分布式锁实现分布式锁得API 初识Zookeeper Zo…

排序篇:外排序(排序文件中的数据)

目录 前言&#xff1a; 一&#xff1a;大体思路 二&#xff1a;分割成有序的小文件 (1)先给代码 (2)解析 三&#xff1a;进行文件归并 (1)主逻辑 (2)归并两个有序文件 四&#xff1a;全部代码 前言&#xff1a; 如果要排序的数据量非常大&#xff0c;内存无法容纳&…

零基础自学软件测试我只用了105天,从月薪3000到15K, 我整理的超全学习指南!

我21年毕业于管理类专业&#xff0c;干了大半年行政打杂&#xff0c;工作平淡无快感。性格较内向&#xff0c;思维严谨独立。喜欢软件测试工作内容的系统性&#xff0c;技术性&#xff0c;丰富性。以上便是转行的最初心理&#xff0c;大家觉得可妥&#xff1f;说干就干去年6月底…

(前期准备工作2)白嫖Replit的免费云服务器搭建属于自己的应用

Replit介绍 Replit(原来是https://repl.it)是一个基于浏览器的云端协同开发平台,可用于构建开发环境、实时协作、托管网络应用等。Replit提供可创建动态或者静态网站的容器,并会自动生成免费https域名(格式为:项目名.用户名.http://repl.co)。这代表着任何人都可以试用…

Kafka的工作原理

一、Kafka是什么&#xff1f; Kafka是一个分布式的基于发布/订阅模式的消息队列。分布式消息队列可以看成是将这种先进先出的数据结构独立部署在服务器上&#xff0c;应用程序可以通过远程访问接口使用它。 二、Kafka的工作机制是什么&#xff1f; 1.基本概念 2.消息模型 发…

WiFi基础学习到实战(六:Beacon帧字段解析)

欢迎大家一起学习探讨通信之WLAN。上节我们基于Android设备分析了WiFi扫描的代码实现&#xff0c;具体执行WiFi网络扫描由WiFi模块实现。WLAN协议定义扫描方式有“被动扫描”和“主动扫描”。本节继续分析“被动扫描”依赖Beacon帧中的字段。 好。我们先来看Android11 WiFi扫描…

会计转行数据分析,可行性多高?

看到这样的问题&#xff0c;第一个想法是想劝退&#xff0c;毕竟通过不明真相的网友身上找自己的未来&#xff0c;这件事听着就不靠谱。转行难&#xff0c;转行做好更好&#xff0c;虽然会计也与数据有关&#xff0c;但是数据分析涉及的技术内容明显有很大的差别。所以&#xf…

重塑职业未来:在竞争激烈的职场上脱颖而出的关键策略

在竞争激烈的职场上&#xff0c;各种职场难题时常出现&#xff0c;如何进行有效沟通、如何应对工作压力、如何提升职业能力等&#xff0c;这都是需要去克服的问题。下面分享一下职场老人的经验&#xff01; 一、你遇到过哪些职场问题&#xff1f;分享一下你是怎么解决的呢&…

Type-C边充电边OTG转接器方案

随着生活水平的提高&#xff0c;大家的电子设备也多了起来&#xff0c;更有甚者会凑齐“全家桶”&#xff0c;手机&#xff0c;平板&#xff0c;笔记本电脑&#xff0c;智能手表&#xff0c;无线耳机&#xff0c;Switch&#xff0c;PS5&#xff0c;一样不落。那么多的电子设备&…

hibernate入门项目(一)

本节我们将演示如何搭建一个 Hibernate 工程。 搭建 Hibernate 工程需要以下 7 步&#xff1a; 下载 Hibernate 开发包 新建工程 创建数据库表 创建实体类 创建映射文件 创建 Hibernate 核心配置文件 测试 1. 下载 Hibernate 开发包 浏览器访问 Hibernate 官网 下载 Hibern…

520告白日!小红书关键词热度查询,今年的心动密码是什么?

520&#xff0c;又是一个有爱的日子&#xff0c;人们借机表达爱意的日子&#xff0c;品牌不会错过的好时机。今年520什么东西比较热呢&#xff1f;消费者比较关注什么&#xff1f;品牌有什么样动作&#xff1f;下面&#xff0c;借助小红书关键词热度查询、热词排行榜&#xff0…

基于C3D卷积神经网路的动作识别

对于基于视频分析的问题&#xff0c;2D卷积&#xff08;卷积核为二维&#xff09;不能很好得捕获时序上的信息&#xff0c;因此《3D convolutional neural networks for human action recognition》 这片论文提出了3D卷积并用于行为识别的&#xff0c;论文中将其用于行为识别&a…

商家中心之java商城 开源java电子商务Spring Cloud+Spring Boot+mybatis+MQ+VR全景+b2b2c

1. 涉及平台 平台管理、商家端&#xff08;PC端、手机端&#xff09;、买家平台&#xff08;H5/公众号、小程序、APP端&#xff08;IOS/Android&#xff09;、微服务平台&#xff08;业务服务&#xff09; 2. 核心架构 Spring Cloud、Spring Boot、Mybatis、Redis 3. 前端框架…