版本
- JDK 8
前言
Optional
是 Java 8 中引入的一个有趣类,用于表示一个值存在或不存在。它的设计目的是解决空指针异常(NullPointerException
)问题,使得程序更加健壮、简洁。
先看一个小案例
- 大家看一下下面的代码是否存在问题?聪明的小伙伴也许都看出来了,代码没有进行判空检验,异常的场景会抛出
NullPointerException
异常。
java
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
- 但是如果加上判空校验,那么我们代码可能就会变成下面这样又臭又长的情况:
java
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();
}
}
}
}
- 那么我们方法可以优化上面这段代码呢?答案当然是肯定的,它就是我们今天要介绍的主角
Java8 引入的 Optional
,接下来就让我们一起看看Optional
的魅力以及如何优化上文中又臭又长的代码。
Optional
Optional
创建
Optional
提供三种方式来创建Optional
对象:
java
// 创建包含值的 Optional 对象
Optional<String> nonEmptyOptional = Optional.of("Hello");
// 创建一个空的 Optional 对象
Optional<String> emptyOptional = Optional.empty();
// 创建包含可能为空的值的 Optional 对象
Optional<String> nullableOptional = Optional.ofNullable(null);
Optional
常用方法
isPresent()
:检查值是否存在。get()
:获取值,如果值不存在会抛出 NoSuchElementException 异常。orElse(T other)
:获取值,如果值不存在则返回指定的默认值。orElseGet(Supplier<? extends T> other)
:获取值,如果值不存在则返回由 Supplier 提供的默认值。orElseThrow(Supplier<? extends X> exceptionSupplier)
:获取值,如果值不存在则抛出由 Supplier 提供的异常。
java
// 检查值是否存在
System.out.println("nonEmptyOptional is present: " + nonEmptyOptional.isPresent());
System.out.println("emptyOptional is present: " + emptyOptional.isPresent());
System.out.println("nullableOptional is present: " + nullableOptional.isPresent());
// 获取值
System.out.println("nonEmptyOptional value: " + nonEmptyOptional.get());
// 值为空是返回指定异常
nullableOptional.orElseThrow(() -> new IllegalStateException("field is not present"));
orElse(T other)
和 orElseGet(Supplier<? extends T> other)
- 两者都是在值不存在时返回默认值,但还是有一些差异:1、接受参数不同 2、某些场景写法中存在性能问题(注意点)
Optional
为 null
java
public class TestMain {
public static void main(String[] args) {
Optional<String> nullableOptional = Optional.ofNullable(null);
System.out.println(nullableOptional.orElse(defaultStr()));
System.out.println(nullableOptional.orElseGet(() -> {
System.out.println("执行 defaultStr 方法 orElseGet");
return "defaultStr";
}));
}
public static String defaultStr() {
System.out.println("执行 defaultStr 方法");
return "defaultStr";
}
}
// 输出
执行 defaultStr 方法
defaultStr
执行 defaultStr 方法 orElseGet
defaultStr
Optional
不为 null
java
public class TestMain {
public static void main(String[] args) {
Optional<String> nullableOptional = Optional.ofNullable("123");
System.out.println(nullableOptional.orElse(defaultStr()));
System.out.println(nullableOptional.orElseGet(() -> {
System.out.println("执行 defaultStr 方法 orElseGet");
return "defaultStr";
}));
}
public static String defaultStr() {
System.out.println("执行 defaultStr 方法");
return "defaultStr";
}
}
// 输出
执行 defaultStr 方法
123
123
- 对比两种情况,我们发现
orElse(T other)
无论Optional
是否null
都会执行传入的函数获取结果值,在一些高并发的场景会造成额外的性能浪费,应尽可能选择使用orElseGet(Supplier<? extends T> other)
。
map
和 flatMap
map
和flatMap
可以将当前值传入到参数函数中,并返回一个Optional
对象,两者唯一的区别在于flatMap
不会再次包装,即传入函数返回值为Optional
类型,具体可以参考下面的例子:
java
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
// flatMap 不会主动包装为 Optional
return Objects.requireNonNull(mapper.apply(value));
}
}
- 示例:
java
@Builder
@Getter
class User implements Serializable {
private String name;
private Integer age;
public Optional<Integer> getAge() {
return Optional.ofNullable(age);
}
}
public class TestMain {
public static void main(String[] args) {
User user = User.builder().name("ming").age(null).build();
Optional.ofNullable(user).map(User::getName).orElse("unknown");
Optional.ofNullable(user).flatMap(User::getAge).orElse(0);
}
}
- 了解
map
和flatMap
方法十分重要,因为这是我们后文实现链式调用的关键。 - 注:
get
方法中一部分返回了Optional
类型,一方面是为了演示,另一个方面可以通过这种方式可以让阅读我们代码的人明确的感知到这个字段的值可能为null
你需要谨慎处理。
filter
过滤
Optional
类也提供了按条件过滤值的方法,filter()
接受一个 Predicate 参数,返回测试结果为 true 的值。如果测试结果为 false,会返回一个空的Optional
。
java
Optional.ofNullable(user).filter(t -> t.getName().contains("test")).orElse("unknown");
如何优化文章开头的代码
- 核心点在于使用
Optional
实现链式调用,首先我们需要对User
类做一些小小的改造。
java
@Builder
@Getter
class User implements Serializable {
private String name;
private Integer age;
private Address address;
public Optional<Integer> getAge() {
return Optional.ofNullable(age);
}
public Address getAddress() {
return address;
}
}
class Address {
private Country country;
public Optional<Country> getCountry() {
return Optional.ofNullable(country);
}
}
class Country {
private String isocode;
public Optional<String> getCountry() {
return Optional.ofNullable(isocode);
}
}
public class TestMain {
public static void main(String[] args) {
User user = User.builder().name("ming").age(null).build();
Optional.ofNullable(user).map(User::getAddress)
.flatMap(Address::getCountry)
.flatMap(Country::getIsocode)
.orElse("unknown");
}
}
- 通过使用
Optional
重构,我们代码的可读性和健壮性都有了很大的提升。
Java9 中的增强
- Java 9 为 Optional 类添加了三个方法:
or()、ifPresentOrElse() 和 stream()
。 or()
:与 orElse() 和 orElseGet() 类似,它们都在对象为空的时候提供了替代情况。or() 的返回值是由 Supplier 参数产生的另一个 Optional 对象。ifPresentOrElse()
:需要两个参数:一个 Consumer 和一个 Runnable。如果对象包含值,会执行 Consumer 的动作,否则运行 Runnable。stream()
:通过把实例转换为 Stream 对象,让我们从广大的 Stream API 中受益。如果没有值,它会得到空的 Stream;有值的情况下,Stream 则会包含单一值。
总结
Optional
是 Java 8 中引入的一个有趣类,它的设计目的是解决空指针异常(NullPointerException
)问题,我们应该好好掌握它,从而让我们在程序代码中更加优雅的处理空指针异常(NullPointerException
)问题,使我们的代码具备更好的可读性以及更加健壮。
个人简介
👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.
🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。
🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。
💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。
🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。
📖 保持关注我的博客,让我们共同追求技术卓越。