讲讲Java8的Optional类

【Java 新特性系列】Java 8 Optional 类完全指南 Java 8 引入的Optional类,是一个可以包含或不包含非空值的容器对象。它的目的是为了提供一种更优雅的方式来处理可能为null的值,从而避免直接使用null值可能引发的。使用Optional类可以显著改善代码的可读性和健壮性。防止:使用Optional可以明确地要求用户在使用变量之前处理null情况;增强代码可读性:通过使用Optional的方法,代码的可读性和意图更加明确;Optional类型在 Java 8 的流(Stream)操作中被广泛使用,提供了更复杂的条件查询和变换功能。 阅读详情

前言

Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava。Java 8 才成为类库中的一部分。

入门

Optional 是一个封装值的类,用于保存类型为 T 的值;本质上,Optional 就是一个容器。

举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car,当变量存在时,Optional 类只是对 Car 的简单封装。变量不存在时,会使用 Optional.empty() 方法返回空的 Optional 对象。如下所示:

在这里插入图片描述

但是 null 引用和 Optional.empty() 有什么本质区别?从语义上,它们可以当成一回事儿,但实际上差别非常大:如果尝试解引用一个 null,一定会触发 NullPointerException,不过使用 Optional.empty() 是一个有效的对象。

下面我们来看一下 Optional 提供的功能。

创建

说到 Optional 的功能,我们首先要了解 Optional 实例的创建。

空Optional

正如前文提到,你可以通过静态工厂方法 Optional.empty,创建一个空的 Optional 对象:

Optional<Car> option = Optional.empty();

因为 empty() 本身代表的就是空对象,所以调用 get 方法会抛出 NoSuchElementException 异常。

非空Optional

你还可以使用静态工厂方法 Optional.of ,依据一个非空值创建一个 Optional 对象:

Optional<Car> optional = Optional.of(car);

如果 car 是一个 null,这段代码会立即抛出一个 NullPointerException,而不是等到你试图访问 car 的属性值时才返回一个错误。

可为null的Optional

最后,使用静态工厂方法 Optional.ofNullable,你可以创建一个允许 null 值的 Optional 对象:

Optional<Car> optional = Optional.ofNullable(car);

如果 car 是 null,那么得到的 Optional 对象就是个空对象。我们可以查看一下它的实现原理:

public static <T> Optional<T> ofNullable(T value) {
	return value == null ? empty() : of(value);
}

根据它的实现方式,我们可知,传入的值是空值时,会返回 Optional.empty() 空对象。这有利于我们封装那些可能为 null 的值。例如,有一个 Map<String, Object> 实例,访问 key 索引时,如果没有与 key 关联的值,则会返回一个 null。因此,我们可以使用 Optional.ofNullable 方法封装返回值:

Optional<Object> value = Optional.ofNullable(map.get("key"));
// 可以由上代码代替 Object value = map.get("key");

这样可以将潜在的 null 隐患替换为空的 Optional 对象。

操作

我们创建了 Optional 实例后,需要对该实例进行操作。

isPresent & get

在 Optional 类中,isPresent 方法对 Optional 实例进行判断,是否包含值,如果存在值,就返回 true,否则返回 false;与之相对的是 isEmpty 方法Optional 类中还有 get 方法,它是用来获取 Optional 实例中的值。

Optional<String> optional = Optional.of("is present");
if (optional.isPresent()) {
	System.out.println("the value is " + optional.get());
}

isPresent 与 get 一般组合使用来避免 NullPointerException:

public boolean isPresent() {
	return value != null;
}
public T get() {
	if (value == null) {
		throw new NoSuchElementException("No value present");
	}
	return value;
}

从源码中可看出,get 方法在取值时,要进行判空操作,如果不使用 isPresent 方法,可能会出现空指针异常。但是这种方式和在代码中if(null != value) 没有区别,因此我们要尽量避免使用该组合。

ifPresent

除了 isPresent 的简洁方法,Optional 还提供了接收函数式参数的接口 ifPresent:

public void ifPresent(Consumer<? super T> action) {
	if (value != null) {
		action.accept(value);
	}
}

该方法会接收一个消费型函数。如果 Optional 实例中的值不为空,则调用 Consumer 的 accept 方法对 value 进行消费,若为空则不做处理。上面的例子可以使用 ifPresent 重写:

Optional<String> optional = Optional.of("is present");
optional.isPresent((val) -> System.out.println("the value is " + val));

orElse

我们还可以使用 orElse 方法读取 Optional 中的值。

public T orElse(T other) {
	return value != null ? value : other;
}

使用这种方式可以定义一个默认值,这种方式当遭遇 Optional 中的变量为空时,默认值会作为该方法的返回值。

String optGet = null;
String orElse = Optional.ofNullable(optGet).orElse("Default");
/**
 * 输出结果:
 * Default
 */

orElseGet

如果该方法不够,我们可以使用另一种方式 orElseGet:

public T orElseGet(Supplier<? extends T> supplier) {
	return value != null ? value : supplier.get();
}

该方法与 orElse 的区别就是值不存在时,调用实现 Supplier 接口的方法或Lambda表达式来返回默认值。

String optGet = null;
String orElse = Optional.ofNullable(optGet).orElse(() -> "Default");
/**
 * 输出结果:
 * Default
 */

orElseThrow

orElseThrow方法是在有值时返回其值,无值的时候会抛出由 Supplier 创建的异常。我们看一下它的实现原理:

public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
	if (value != null) {
		return value;
	} else {
		throw exceptionSupplier.get();
	}
}

在 orElseThrow 原理中,会传入一个Lambda表达式或方法,如果值不存在来抛出异常:

class NoValueException extends RuntimeException {
    public NoValueException() {
        super();
    }
    @Override
    public String getMessage() {
        return "No value present in the Optional instance";
    }
}
public static Integer orElseThrow() {
	return (Integer) Optional.empty().orElseThrow(NoValueException::new);
}
public static void main(String[] args) {
	orElseThrow();
}
/**
 * 控制台会抛出异常:
 * Exception in thread "main" xx.NoValueException: No value present in the Optional instance
 */

orElseThrow 与 orElseGet 的区别就是一个在无值的时候抛出异常,一个在无值的时候使用Lambda表达式来实现默认值。

orElseThrow 只是在无值的时候抛出异常,那本身会抛出异常的方法呢?

现在,我们拿 Integer.parseInt(String) 做个例子:

public static Integer toInt(String s) {
	try {
		// 如果String能转换为对应的Integer,将其封装在Optional对象中返回
		return Integer.parseInt(s);
	} catch (NumberFormatException e) {
		return null;	// 返回null 或者抛出异常
	}
}

在将 String 转换为 int 时,如果无法解析到对应的整型,该方法会抛出 NumberFormatException 异常。我们在该方法中使用 try/catch 语句捕获了该异常,不能使用 if 条件判断来控制一个变量的值是否为空。

这时,我们可以使用 Optional 类,来对无法转换的 String 时返回的非法值进行建模,因此,我们可以对上述方法进行改进:

public static Optional<Integer> toInt(String s) {
	try {
		// 如果String能转换为对应的Integer,将其封装在Optional对象中返回
		return Optional.of(Integer.parseInt(s));
	} catch (NumberFormatException e) {
		return Optional.empty();	// 否则返回一个空的 Optional 对象
	}
}

这种返回 Optional 的方式适用很多方法,我们只需要获取被 Optional 包装的值的实例即可。

map

Optional 提供了 map 方法用于从对象中提取信息,它的工作原理如下:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
	Objects.requireNonNull(mapper);
	if (!isPresent())
		return empty();
	else {
		return Optional.ofNullable(mapper.apply(value));
	}
}

map 操作会将提供的函数应用于流的每个元素。我们可以把 Optional 对象看成一种特殊的集合数据,它至多包含一个元素。如果 Optional 包含一个值,那通过实现了 Function 接口的 Lambda 表达式对值进行转换。如果不熟悉 Function 接口,可以参考这篇文章。map 方法示例如下:

class Car {
	private String name;
	private String type;
	...省略getter与setter...
}
Optional<Car> optional = Optional.ofNullable(car);
Optional<String> name = optional.map(Car::getName);

flatMap

我们可以使用 map 方法来从被 Optional 类包装的 Person 类中获取 Car 的名称:

class Person {
	private Optional<Car> car;
	public Person(Car car) {
		this.car = Optional.of(car);
	}
	...省略getter与setter...
}
Person person = new Person(new Car());
Optional<Person> optPerson = Optional.of(person);
Optional<String> name = optPerson.map(Person::getCar)
	.map(Car::getName);

不幸的是,这段代码无法通过编译。为什么呢?optPerson 是 Optional<Person> 类型的变量,调用 map 方法应该没有问题。但 getCar 返回的是一个 Optional<Car> 类型的对象,这意味着 map 操作的结果是一个 Optional<Optional<Car>> 类型的对象。因此,它对 getName 的调用是非法的,因为最外层的 optional 对象包含了另一个 optional 对象的值,而它当然不会支持 getName 方法。

在这里插入图片描述

所以,我们使用 flatMap 方法。该方法接受一个函数作为参数,这个函数的返回值是另一个流。

public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper) {
	Objects.requireNonNull(mapper);
	if (!isPresent()) {
		return empty();
	} else {
		@SuppressWarnings("unchecked")
		Optional<U> r = (Optional<U>) mapper.apply(value);
		return Objects.requireNonNull(r);
	}
}

参照 map 函数,使用 flatMap 重写上述的示例:

Optional<String> name = optPerson.flatMap(Person::getCar).map(Car::getName);

filter

有时候我们需要对 Optional 中的值进行过滤,获得我们需要的结果,我们就可以使用 filter 方法:

public Optional<T> filter(Predicate<? super T> predicate) {
	Objects.requireNonNull(predicate);
	if (!isPresent())
		return this;
	else
		return predicate.test(value) ? this : empty();
}

该方法接受 Predicate 谓词作为参数。如果 Optional 对象的值存在,并且符合谓词的条件,即操作结果为true,filter 方法不做任何改变并返回其值;否则就将该值过滤掉并返回一个空的 Optional 对象。

Optional<String> optionalS = Optional.of("13846901234");
optionalS = optionalS.filter(s -> s.contains("138"));
/**
 * 上述 `filter` 方法满足条件可以返回同一个Optional,否则返回空Optional
 */

总结

Java 8引入的 java.util.Optional<T> 让我们以函数式编程的方式处理 null,防止空指针异常;并支持多种方式用于操作值,比如:map、flatMap 和 filter,这样可以抛弃嵌套的 if-else 代码块,设计更好的 API,代码的可读性也大大提高,但是如果在域模型中使用 Optional,由于没有实现 Serializable 接口,不能进行实例化,也不能作为类的字段。

更多内容请关注公众号「海人为记」,回复「资源」即可获得免费学习资源!

在这里插入图片描述

Java8 新特性之lamdba表达式和Optional 1 Lambda表达式 2 1.1 Lambda语法糖 2 1.1.1 语法 2 1.1.2 Lambda表达式可使用的变量 3 1.1.3 lambda表达式中的this概念 3 1.2 函数式接口- Consumer/Predicate/Function/Supplier 4 1.1.4 什么是函数式接口 4 1.1.5 Consumer接口 5 1.1.6 Function接口 6 1.1.7 Supplier接口 8 1.1.8 Predicate接口 9 1.3 方法引用和构造器 12 1.2.1 方法引用 12 1.2.2 构造器 12 1.4 Stream语法 13 1.3.1 入门案例 13 1.3.2 怎么得到Stream 15 1.3.3 转换Stream 16 1.3.4 汇聚Stream 19 2 Optional类 20 2.1 创建Optional容器 21 2.2 容器简单方法 22 2.3 容器进阶方法 23 1.3.5 ifPresent方法 23 1.3.6 orElseGet和orElseThrow方法 23 1.3.7 filter方法 24 1.3.8 map方法 24 1.3.9 flatMap方法 25 2.4 总结 25 立即下载

相关推荐

《Ventoy 制作系统安装 U 盘的具体步骤》

然后您可以在电脑的磁盘管理中对 U 盘进行快速格式化,选择 FAT32 或 NTFS 文件系统。至此,Ventoy 系统安装 U 盘就制作完成了。您可以在需要安装系统的电脑上,从 U 盘启动,然后选择相应的系统镜像进行安装。安装完成后,将您需要的操作系统镜像文件(如 Windows、Linux 等的 ISO 格式镜像)复制到 U 盘的根目录。在安装程序中,选择您插入的 U 盘设备,确保选择正确,以免误操作。2. 确认选择的 U 盘盘符正确,防止误操作导致其他磁盘数据丢失。点击“安装”按钮,等待安装完成。

jiyiwangluokeji的博客 2666

Map使用技巧

快捷使用防空与初始化。

weixin_45697659的博客 191

鸿蒙之HML语法参考

HML(HarmonyOS Markup Language)是一套类HTML的标记语言,通过组件,事件构建出页面的内容。页面具备数据绑定、事件绑定、条件渲染和逻辑控制等高级能力。

2401_82772199的博客 1649

java map get tostring 空指针_Java基础系列-Optional

作者:唯一浩哥出自:博客园原文:cnblogs.com/V1haoge/p/10755368.html一、概述Optional的引入是为了解决null的问题,那么到底是解决null的什么问题呢?我们知道当我们针对null调用方法的之后,就会抛出空指针异常,Optional就是为了解决这个问题而来的。Optional通过封装目标对象的方式来表示,当我们使用的时候,Optional是必然存在的,因为如...

weixin_33642697的博客 1032

Origin2018(汉化版)在使用科学计数法的时候如何将坐标刻度的0.0改成0

文章目录1 问题2 解决方法 1 问题 先看下面这张图,当我使用科学计数法时,纵坐标出现了0.0的字样,这样看起来很不美观。有些期刊还会有要求,不能出现0.0的格式。 图1 那么该怎么将它改成0呢?首先让我们尝试着用Origin自带的属性,看看能不能设置。 2 解决方法 解决方法一: step1: 双击坐标轴,打开坐标轴属性设置。 step2: 在刻度线标签选项中设置左轴的显示 图2 会发现,按照图2的方式改好之后,其它的小数位也全没了,如果但是有些数字就被覆盖了,这显然不是我

qq_40077565的博客 2万+

OpenCV轮廓匹配实战:5分钟搞定Hu矩形状识别(附完整代码)

本文详细介绍了使用OpenCV进行轮廓匹配的实战技术,重点讲解了Hu矩形状识别在工业质检等领域的应用。通过完整的代码示例和参数调优技巧,帮助开发者在5分钟内实现高效的形状识别,提升工业检测的准确性和效率。

nnn11的博客 675

Map.getOrDefault()以及Optional.ofNullable()

Map.getOrDefault()以及Optional.ofNullable()

weixin_44974020的博客 1330

FlexSim:FlexSim软件介绍与安装

在开始安装FlexSim之前,首先需要从FlexSim官方网站下载安装包。FlexSim提供不同版本的软件,包括教育版、专业版等,确保选择适合您需求的版本。下载页面通常会列出系统要求,包括操作系统、处理器、内存和硬盘空间,确保您的计算机满足这些要求。访问FlexSim官方网站。导航至下载页面。选择适合您需求的FlexSim版本。点击下载按钮,开始下载安装包。在开始构建模型之前,首先需要明确模型的目标。模型目标是指导模型设计和分析的核心,它定义了你希望通过模型解决的问题或探索的系统行为。

kkchenjj的博客 5803

Java8新特性:Optional类(超详解)

本博主将用优快云记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!也许一个人独行,可以走的很快,但是一群人结伴而行,才能走的更远!分享ChatGPT的相关资料文档、最新资讯、玩法和创意,获取第一手信息,一起走在AI时代的最前线!前100名加入可赠送ChatGPT账号!Optional 类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。

weixin_52533007的博客 2410

光学设计仿真软件:Code V_(8).场曲和色差校正

通过以上步骤和代码示例,用户可以更好地理解如何在Code V中进行场曲和色差的校正。这些技术不仅有助于提高光学系统的成像质量,还可以为后续的光学设计提供重要的参考。在实际应用中,用户可以根据具体需求选择合适的校正方法和优化策略,以达到最佳设计效果。

kkchenjj的博客 1784

【信号与系统】Multisim 仿真信号合成与分解

软件 Multisim 进行方波信号的产生、分解和合成,观察信号波形的分解与合成,加深对信号频谱的理解。

AXYZdong的博客 2万+

optional判断是否为空_讲讲Java8的Optional类

前言Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava。Java 8 才成为类库中的一部分。入门Optional 是一个封装值的类,用于保存类型为 T 的值;本质上,Optional 就是一个容器。举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car,当变量存...

weixin_39801356的博客 1万+

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

java的optional类干什么_讲讲Java8的Optional类

前言Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava。Java 8 才成为类库中的一部分。入门Optional 是一个封装值的类,用于保存类型为 T 的值;本质上,Optional 就是一个容器。举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car,当变量存...

weixin_35712036的博客 781

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

java8 optional_讲讲Java8的Optional类

前言Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava。Java 8 才成为类库中的一部分。入门Optional 是一个封装值的类,用于保存类型为 T 的值;本质上,Optional 就是一个容器。举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car,当变量存...

weixin_35653388的博客 214

【有料】讲讲Java8的Optional类

前言 Java 8中引入了Optional类来解决NullPointerException与繁琐的null检查,该类首次出现在Guava。Java 8 才成为类库中的一部分。 入门 Optional是一个封装值的类,用于保存类型为T的值;本质上,Optional就是一个容器。 举例来说,一个人可能有车也可能没有,那么Person类内部car变量就不应该声明为Car,当变量存在时,Optional类只是对Car的简单封装。变量不存在时,会使用Optional.e...

daobuxinzi的博客 423

optional 解决空指针_讲讲Java8的Optional类

前言Java 8中引入了 Optional 类来解决 NullPointerException 与繁琐的 null 检查,该类首次出现在 Guava。Java 8 才成为类库中的一部分。入门Optional 是一个封装值的类,用于保存类型为 T 的值;本质上,Optional 就是一个容器。 举例来说,一个人可能有车也可能没有,那么 Person 类内部 car 变量就不应该声明为 Car,当变量...

weixin_31993699的博客 550

Java Optional orElse,orElseGet,orElseThrow()用法及代码示例

Java Optional orElse,orElseGet,orElseThrow()用法及代码示例 我们都知道,Optional类是Java8为了解决null值判断问题,使用Optional类可以避免显式的判断null值(null的防御性检查),避免null导致的NPE(NullPointerException) 。 在介绍这三个方法之前,先讲讲 Optional 的 ofNullable() 方法,因为他们常常搭配一起使用。 我们来看看 ofNullable() 方法的源码: /** * A con

weixin_44389218的博客 8419

面试官问的Java8的新特性你真的了解了吗?

Java 8是Java自Java 5(发布于2004年)之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。我们平时开发也主要使用的是这个版本进行开发,今天我们就来讲讲这些新特性都有哪些以及在平时开发是怎么样使用的。 一、Lambda表达式和函数式接口 在Java8中最经常看到和被猿人们使用的就是Lambda表达式了,也是面试过程中比较平常的回答。那它是怎么使用的呢? 最简单的Lambda表达式可由逗号分隔的参数列表、->符号和语句块组成,如果Lambda表达式需要更.

weixin_43709538的博客 726

【Java面试神题】谢飞机大战面试官:从Optional到Prometheus,一战定乾坤!

Optional类:用于减少空指针异常,提供orElse()map()flatMap()等链式调用API。Maven依赖管理:依赖传递、版本仲裁、exclusion排除依赖。:DSL语法、灵活性、插件系统更强。谢飞机虽然回答得磕磕绊绊,但核心知识点都答出来了。如果你也在准备大厂Java面试,建议重点掌握这些高频考点,并结合实际项目说出你的理解和优化方案。祝你早日拿下Offer!

qq_54002085的博客 361

Java8之Consumer、Supplier、Predicate和Function攻略

今天我们还讲讲Consumer、Supplier、Predicate、Function这几个接口的用法,在 Java8 的用法当中,这几个接口虽然没有明目张胆的使用,但是,却是润物细无声的。为什么这么说呢? 这几个接口都在 java.util.function 包下的,分别是Consumer(消费型)、supplier(供给型)、predicate(谓词型)、function(功能性),相信有了后...

好好学java 5774

面试又挂了,你理解了 Java 8 的 Consumer、Supplier、Predicate和Function吗?

点击上方好好学java,选择星标公众号重磅资讯、干货,第一时间送达>>广而告之:打卡有奖活动火热进行中,快来参与吧,来了就有奖今天我们还讲讲Consu...

好好学java 4842

第18章:随堂复习与企业真题(JDK8-17新特性)

略。java.util.function包下定义了丰富的好函数式接口。有4类基础的函数式接口:消费型接口:Consumer void accept(T t)供给型接口:Supplier T get()函数型接口:Function<T,R> R apply(T t)判断型接口:Predicate boolean test(T t)

qq_51215835的博客 1128
上一篇: 快速了解JavaScript的BOM模型
hireny
博客等级 码龄12年 1粉丝 12原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值