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. sql-labs less34--less41

    less 34 汉 addslashes() 函数返回在预定义的字符前添加反斜杠的字符串. 预定义字符是: 单引号(') 双引号(") 反斜杠(\) NULL 提示:该函数可用于为存储在数据 ...

  2. CSS 常用样式-盒模型属性

    盒模型又叫框模型,包含了五个用来描述盒子位置.尺寸的属性,分别是宽度 width.高度 height.内边距 padding. 边框 border.外边距 margin. 常见盒模型区域: • 盒模型 ...

  3. uni-app中调用高德地图去设置点和轨迹

    盒子部分 <view style="width: 100%; height: 100%" id="busContainer"> </view& ...

  4. org.nutz.http.Http忽略https SSL证书验证

    访问的是一个https get请求,报错需要SSL证书验证,以下方法直接跳过 boolean check = Http.disableJvmHttpsCheck(); // 忽略https的证书检查

  5. gstreamer样例

    #!/bin/bash xrandr -s 0 --output DSI-1 xrandr -s 0 --output HDMI-1 xrandr --fb 1024x600 --output DSI ...

  6. java中的4种引用和GC Roots

    https://juejin.im/post/5d06de9d51882559ee6f4212?utm_source=gold_browser_extension 1.首先,四种引用如下: Final ...

  7. 关于UPD章节学习的一些感想

    课程看到了UDP, 首先了解UDP的原理模型. 服务端,首先是实例QUdpSocket,也就是UDP套接字,然后,需要绑定.绑定一个任意IP地址,分为IPV4和IPV6.自行选择课程中讲解时绑定的是I ...

  8. lc.209 长度最小的子数组

    题目 给定一个含有 n 个正整数的数组和一个正整数 target . 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, n ...

  9. 转载C#加密方法

    方法一:    //须添加对System.Web的引用     using System.Web.Security;          ...          /// <summary> ...

  10. 在.net core中开发web页面,更新html代码刷新不生效的问题

    因为在.net core之后的版本,程序都是以控制台应用程序的方式存在,所以一些老的项目升级后,会出现这样的情况, 解决方法,在nuget中引入 Microsoft.AspNetCore.Mvc.Ra ...