[Java SE]Java版本特性解读:java.util.Optional [JDK1.8-]
1 概述
本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空(null/empty)。
Optional 是 Java 实现函数式编程的强劲一步,并且帮助在范式中实现。但是 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-]的更多相关文章
- Java SE 8 新增特性
Java SE 8 新增特性 作者:Grey 原文地址: Java SE 8 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...
- Java SE 9 新增特性
Java SE 9 新增特性 作者:Grey 原文地址: Java SE 9 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new_ ...
- Java SE 10 新增特性
Java SE 10 新增特性 作者:Grey 原文地址:Java SE 10 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 11 新增特性
Java SE 11 新增特性 作者:Grey 原文地址:Java SE 11 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 12 新增特性
Java SE 12 新增特性 作者:Grey 原文地址:Java SE 12 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 13 新增特性
Java SE 13 新增特性 作者:Grey 原文地址:Java SE 13 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 16 新增特性
Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 17 新增特性
Java SE 17 新增特性 作者:Grey 原文地址:Java SE 17 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 19 新增特性
Java SE 19 新增特性 作者:Grey 原文地址: 博客园:Java SE 19 新增特性 CSDN:Java SE 19 新增特性 源码 源仓库: Github:java_new_featu ...
- Java SE 6 新特性: Java DB 和 JDBC 4.0
http://www.ibm.com/developerworks/cn/java/j-lo-jse65/index.html 长久以来,由于大量(甚至几乎所有)的 Java 应用都依赖于数据库,如何 ...
随机推荐
- java中锁的应用(synchronized)
在面试菜鸟的时候碰到的锁的编程问题,没答好,记录一下: package com.xielu.test; /** * Hello world! * */ public class App { priva ...
- 第15章 授权:保护您的应用程序(ASP.NET Core in Action, 2nd Edition)
本章包括 使用授权控制谁可以使用你的应用 对策略使用基于声明的授权 创建自定义策略以处理复杂的需求 根据所访问的资源授权请求 隐藏用户未经授权访问的Razor模板中的元素 在第14章中,我向您展示了如 ...
- linux 离线安装mysql 配置开机自启动
系统版本:centos7.8 | mysql版本:5.7.35 安装配置mysql数据库 mysql数据库配置开机自启动 1. 安装配置mysql数据库 mysql版本:5.7.35 点击下载 提取码 ...
- springboot 注解属性配置
参考: https://blog.csdn.net/ouyangguangfly/article/details/106646378 https://www.cnblogs.com/cbzj/p/94 ...
- C# DevExpress gridview 字符串尾部带数字如何排序
我们经常遇到这样的问题,字符串尾部带数字,如何正确排序; 首先设置GridView ,Columns 的相关列,设置属性中,SortMode为Custom 解决思路,把字符串尾缀数字,分离出来.先比较 ...
- shell编程【一】
参考网址:[Linux命令大全][]:https://linux265.com/course/linux-commands.html 为什么要学习shell指令? 学习shell指令的本身也是在了解L ...
- linux命令基本操作
chmod 命令 : chmod -R 755 tools_command/ 解释 : -R 为递归遍历tools_command文件夹, chmod 755修改权限: 查看所有用户 1.cat /e ...
- Deepin20安装pip3和Pydev插件
1. 安装jdk(见前面) 2. 安装eclipse(见前面) 3. Deepin20里已经有python3.7.3和python2.7.16,我们可以直接使用3.7.3,也是默认的python3.还 ...
- AssetBuddle(一)
一·含义 一个压缩文件,针对于特定平台的资产压缩包.资产包括模型,贴图,预制体,音效,材质球等,注意不包括c#脚本. 二·作用 相对于resource文件夹下面的东西打包后定死只读,无法修改. AB包 ...
- rabbit.bat server启动闪退,rabbitmq (ArgumentError) argument error,rabbitmq页面无法开启解决方案
步骤1:下载rabbitmq-plugins rabbitmq-plugins enable rabbitmq_managemen 步骤2:rabbitmq-service移除服务 rabbitmq- ...