1 概述

本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空(null/empty)

OptionalJava 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 Optional 的意义显然不止于此。

我们从一个简单的用例开始。在 Java 8 之前,任何访问对象方法或属性的调用都可能导致NullPointerException

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();

在这个小示例中,如果我们需要确保不触发异常,就得在访问每一个值之前对其进行明确地检查:

if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}

2 样例源码

你看到了,这很容易就变得冗长,难以维护。

为了简化这个过程,我们来看看用 Optional 类是怎么做的。从创建和验证实例,到使用其不同的方法,并与其它返回相同类型的方法相结合,下面是见证Optional 奇迹的时刻。

WithoutOptionalTest

package test.java.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test; /**
* @create-time 2022/12/29 11:00
* @description 没有 Optional 会有什么问题?
* 我们来模拟一个实际的应用场景。
* 小王第一天上班,领导老马就给他安排了一个任务,要他从数据库中根据会员 ID 拉取一个会员的姓名,然后将姓名打印到控制台。
* 虽然是新来的,但这个任务难不倒小王,于是他花了 10 分钟写下了本段代码(WithoutOptionalTest)
* @reference-doc
* [1] 干货,一文彻底搞懂 Java 的 Optional - CSDN - https://blog.csdn.net/qing_gee/article/details/104767082
*/
@Slf4j
public class WithoutOptionalTest {
/**
* 由于当前 ID 的会员不存在,故 getMemberByIdFromDB() 返回了 null 来作为没有获取到该会员的结果。
* 意味着:在打印会员姓名的时候要先对 mem 判空;否则,就会抛出 NPE 异常。
* 不信?让小王把 if (mem != null) 去掉试试,控制台立马打印错误堆栈给你颜色看看。
*/
@Test
public void withoutOptionalTest(){
Member mem = getMemberByIdFromDB();
if (mem != null) {
log.info("member.name: {}", mem.getName());
}
Assert.assertTrue(true);
} public static Member getMemberByIdFromDB() {
// DB查询结果:当前 ID 的会员不存在
return null;
} class Member {
private String name; public String getName() {
return name;
} public void setName(String name) {
this.name = name;
}
}
}

OptionalTest

package test.java.util;

import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.Test; import java.util.Collections;
import java.util.List;
import java.util.Optional; /**
* @create-time 2022/12/29 10:59
* @description Optional 是如何解决这个问题的?
* 小王把代码提交后,就兴高采烈地去找老马要新的任务了。
* 本着虚心学习的态度,小王请求老马看一下自己的代码,于是老王就告诉他应该尝试一下 Optional,可以避免没有必要的 null 值检查。
* 现在,让我们来看看小王是如何通过 Optional 来解决上述问题的。
* @reference-doc
* [1] 干货,一文彻底搞懂 Java 的 Optional - CSDN - https://blog.csdn.net/qing_gee/article/details/104767082
*/
@Slf4j
public class OptionalTest {
/**
* getMemberByIdFromDB() 方法返回了 Optional<Member> 作为结果,这样就表明 Member 可能存在,也可能不存在,这时候就可以:
* 在 Optional 的 ifPresent() 方法中使用 Lambda 表达式来直接打印结果。
*
* Optional 之所以可以解决 NPE 的问题,是因为:它明确的告诉我们,不需要对它进行判空。它就好像十字路口的路标,明确地告诉你该往哪走。
*/
@Test
public void withOptionalTest(){
Optional<Member> optional = getMemberByIdFromDB();
optional.ifPresent(mem -> {
log.debug("member.name: {}", mem.getName());
});
log.debug("optional.isPresent : {}", optional.isPresent());
log.debug("optional.get() : {}", optional.get()); optional = Optional.empty();
Member member = optional.orElse(new Member("optional 为空时设置的默认会员信息"));
log.debug("member : {}", member);
//如果存在该值,返回包含的值,否则抛出由 Supplier 继承的异常
//optional.orElseThrow(() -> { return new RuntimeException("optional为空时出现异常!"); }); Assert.assertTrue(true);
} /**
* ofNullable() 方法内部有一个三元表达式:
* 如果为参数为 null,则返回私有常量 EMPTY;
* 否则,使用 new 关键字创建了一个新的 Optional 对象——不会再抛出 NPE 异常了。
*/
@Test
public void ofNullableTest(){
//true
List<Member> list = Collections.EMPTY_LIST;
Optional<List<Member>> optionalList = Optional.ofNullable(list);
boolean present = optionalList.isPresent();
log.debug("present : {}", present);
Assert.assertTrue(true);
} @Test
public void createOptionalObject01(){
//可以使用静态方法 empty() 创建一个空的 Optional 对象
Optional<String> empty = Optional.empty();
log.debug(empty.toString()); // 输出:Optional.empty String str = null;
Optional<String> optionalStr = Optional.ofNullable(str);
boolean isPresent2 = optionalStr.isPresent();
log.debug("optionalStr : {}", optionalStr); // 输出:Optional.empty
log.debug("isPresent2 : {}", isPresent2); // 输出:false Assert.assertTrue(true);
} @Test
public void createOptionalObject02(){
//可以使用静态方法 of() 创建一个非空的 Optional 对象
Optional<String> opt = Optional.of("沉默王二");
log.debug(opt.toString()); // 输出:Optional[沉默王二] //当然了,传递给 of() 方法的参数必须是非空的,也就是说不能为 null,否则仍然会抛出 NullPointerException。
// String name = null;
// Optional<String> optnull = Optional.of(name);// java.lang.NullPointerException Assert.assertTrue(true);
} @Test
public void createOptionalObject03(){
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
log.debug("optOrNull : {}", optOrNull); // 输出:Optional.empty Assert.assertTrue(true);
} @Test
public void OrElseTest(){
// 有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了。
// orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回 原本的值;否则返回默认值。该方法的参数类型和值得类型一致。
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
log.debug("name : {}", name); // 输出:沉默王二 String nullName2 = "hello";
String name2 = Optional.ofNullable(nullName2).orElse("沉默王二");
log.debug("name2 : {}", name2); // 输出:hello Assert.assertTrue(true);
} @Test
public void orElseGetTest(){
//orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数。
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()-> { return "沉默王二"; });
log.debug("name : {}", name); // 输出:沉默王二 Assert.assertTrue(true);
} public static Optional<Member> getMemberByIdFromDB() {
boolean hasName = true;
if (hasName) {
return Optional.of(new Member("沉默王二"));
}
return Optional.empty();
} } class Member {
private String name; public Member(String name) {
this.name = name;
} public String getName() {
return name;
} // getter / setter @Override
public String toString() {
return "Member{" +
"name='" + name + '\'' +
'}';
}
}

X 参考文献

[Java SE]Java版本特性解读:java.util.Optional [JDK1.8-]的更多相关文章

  1. Java SE 8 新增特性

    Java SE 8 新增特性 作者:Grey 原文地址: Java SE 8 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...

  2. Java SE 9 新增特性

    Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...

  3. Java SE 10 新增特性

    Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  4. Java SE 11 新增特性

    Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  5. Java SE 12 新增特性

    Java SE 12 新增特性 作者:Grey 原文地址:Java SE 12 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  6. Java SE 13 新增特性

    Java SE 13 新增特性 作者:Grey 原文地址:Java SE 13 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  7. Java SE 16 新增特性

    Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  8. Java SE 17 新增特性

    Java SE 17 新增特性 作者:Grey 原文地址:Java SE 17 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...

  9. Java SE 19 新增特性

    Java SE 19 新增特性 作者:Grey 原文地址: 博客园:Java SE 19 新增特性 CSDN:Java SE 19 新增特性 源码 源仓库: Github:java_new_featu ...

  10. Java SE 6 新特性: Java DB 和 JDBC 4.0

    http://www.ibm.com/developerworks/cn/java/j-lo-jse65/index.html 长久以来,由于大量(甚至几乎所有)的 Java 应用都依赖于数据库,如何 ...

随机推荐

  1. 【python】第一模块 步骤五 第一课、内存管理机制

    第一课.内存管理机制 一.课程介绍 1.1 课程概要 课程概要 赋值语句的内存分析 垃圾回收机制 内存管理机制 课程目标 掌握赋值语句内存分析方法 掌握id()和is()的使用 了解python的垃圾 ...

  2. CentOS 8.x下编译php 7.4、php5.6、php5.3多版本报错处理教程

    一.编译安装php 7.4.x 参考CentOS 8.0.1905编译安装Nginx1.16.1+MySQL8.0.18+PHP7.3.10 1.安装编译工具及库文件(使用yum命令安装) yum i ...

  3. JNI接口的实现

    JNI接口的实现 什么是JNI 说明:JNI 是 Java Native Interface 的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++,但是它并不妨碍你使用 ...

  4. github使用流程

    前期硬件准备工作(电脑相关配置): 1.下载git软件,傻瓜式安装 https://git-scm.com/download/win 2.设置你的用户名称与邮件地址 git config --glob ...

  5. 2003031118-李伟-Python数据分析第三周作业-第一次作业

    项目 NumPy数值计算基础 博客名称 2003031118-李伟-Python数据分析第三周作业-第一次作业 课程班级博客链接 https://edu.cnblogs.com/campus/pexy ...

  6. CentOS7 进入修复模式的办法

    有时候配置Centos文件修改错误会导致系统无法登录,可以通过修复模式进行单用户运行模式,进行修复. 具体操作如下: 1.重启服务器,在选择内核界面使用上下箭头移动 2.选择内核并按"e&q ...

  7. Angular架构学习

    定义 Angular 是一个用 HTML 和 JavaScript 或者一个可以编译成 JavaScript 的语言(例如 Dart 或者 TypeScript ),来构建客户端应用的框架. 写 An ...

  8. ABP vNext微服务架构详细教程(补充篇)——单层模板

    1. 简介 在之前的<ABP vNext微服务架构详细教程>系列中,我们已经构建了完整的微服务架构实例,但是在开发过程中,我们会发现每个基础服务都包含10个类库,这是给予DDD四层架构下A ...

  9. ggplot2: display every nth value on discrete axis

    every_nth = function(n) { return(function(x) {x[c(TRUE, rep(FALSE, n - 1))]}) } ggplot(mpg, aes(x = ...

  10. 007使用IDEA开发

    007使用IDEA开发 1.什么叫IDE? 集成开发环境(IDE,Integrated Development Environment)是用于提供程序开发环境的应用程序,一般包括代码编辑器.编译器.调 ...