Optional
字数: 0 字 时长: 0 分钟
1. 概述
在 Java 开发中,空指针异常 NullPointerException 非常常见,因此经常需要进行非空判断。
例如:
Author author = getAuthor();
if (author != null) {
System.out.println(author.getName());
}如果对象内部还包含其他对象,非空判断可能会越来越多,导致代码出现大量嵌套:
对象
↓
判断是否为空
↓
获取属性
↓
再次判断是否为空
↓
继续处理JDK 8 引入了 Optional,用于表示:
一个值可能存在,也可能不存在。
通过 Optional 提供的方法,可以减少直接操作 null 时产生的空指针问题,并且很多函数式编程相关 API 也会使用 Optional。
2. 创建 Optional
Optional 可以理解为一个容器,用来包装一个可能为空的数据。
最常用的创建方式:
Optional.ofNullable(value)2.1 ofNullable()
Optional.ofNullable() 无论传入的值是否为 null,都可以正常创建 Optional。
public class OptionalDemo {
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> authorOptional = Optional.ofNullable(author);
authorOptional.ifPresent(
value -> System.out.println(value.getName())
);
}
private static Author getAuthor() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return null;
}
}即使 getAuthor() 返回 null,也不会因为创建 Optional 而出现空指针异常。
实际使用时,也可以直接让方法返回 Optional:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.ifPresent(
author -> System.out.println(author.getName())
);
}
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return Optional.ofNullable(author);
}这样调用方看到返回值:
Optional<Author>就能够明确知道:
这个方法可能返回一个 Author,也可能没有数据。
在实际开发中,数据库查询也可能返回空数据。MyBatis 3.5 之后可以支持方法返回 Optional,可以直接将 DAO / Mapper 方法的返回值声明为 Optional<T>。
2.2 of()
如果可以确定数据一定不为 null,可以使用:
Optional.of(value)例如:
public static void main(String[] args) {
Author author = getAuthor();
Optional<Author> authorOptional = Optional.of(author);
authorOptional.ifPresent(
value -> System.out.println(value.getName())
);
}
private static Author getAuthor() {
return new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
}需要注意:
Optional.of()不能接收null,否则会直接抛出NullPointerException。
例如:
Optional<Author> optional = Optional.of(null);会报错:

因此,如果数据可能为空,通常使用:
Optional.ofNullable(value)更加合适。
2.3 ofNullable() 原理
查看 ofNullable() 的源码:

核心逻辑可以理解为:
return value == null
? Optional.empty()
: Optional.of(value);也就是:
value == null
↓
Optional.empty()
value != null
↓
Optional.of(value)2.4 empty()
Optional.empty() 用于创建一个内部没有数据的 Optional。
例如:
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return author == null
? Optional.empty()
: Optional.of(author);
}实际上这段代码可以直接使用:
return Optional.ofNullable(author);因此一般情况下,直接使用 ofNullable() 会更加简洁。
3. 安全消费值
3.1 ifPresent()
获取到 Optional 后,如果只希望在数据存在时执行某个操作,可以使用:
ifPresent()它会判断内部是否存在数据:
存在数据
↓
执行 Consumer
没有数据
↓
什么都不执行例如:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional.ifPresent(
author -> System.out.println(author.getName())
);
}
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return Optional.ofNullable(author);
}相比:
if (author != null) {
System.out.println(author.getName());
}可以直接通过 Optional 对数据进行安全消费。
4. 获取值
4.1 get()
可以通过 get() 直接获取 Optional 中的数据:
Optional<Author> authorOptional = getAuthorOptional();
Author author = authorOptional.get();
System.out.println(author);例如:
private static Optional<Author> getAuthorOptional() {
return Optional.ofNullable(null);
}此时再执行:
authorOptional.get();会抛出异常。

因此:
不推荐在无法确定 Optional 一定有值时直接使用
get()。
5. 安全获取值
相比直接使用 get(),可以通过 Optional 提供的方法处理数据为空的情况。
5.1 orElseGet()
orElseGet() 用于:
有值就返回原来的值,没有值就通过 Supplier 创建一个默认值。
完整写法:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
Author author = authorOptional.orElseGet(
new Supplier<Author>() {
@Override
public Author get() {
return new Author();
}
}
);
}
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return Optional.ofNullable(null);
}Lambda 简化:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
Author author = authorOptional.orElseGet(
() -> new Author(
1L,
"锐雯",
33,
"一个从菜刀中明悟哲理的祖安人",
null
)
);
}执行逻辑:
Optional 有值
↓
返回原来的值
Optional 没有值
↓
执行 Supplier
↓
返回默认值例如:
有蒙多
↓
返回蒙多
没有数据
↓
返回锐雯5.2 orElseThrow()
orElseThrow() 用于:
有值就返回数据,没有值就抛出指定异常。
完整写法:
Optional<Author> authorOptional = getAuthorOptional();
Author author = authorOptional.orElseThrow(
new Supplier<RuntimeException>() {
@Override
public RuntimeException get() {
return new RuntimeException("数据为空");
}
}
);Lambda 简化:
Author author = authorOptional.orElseThrow(
() -> new RuntimeException("数据为空")
);这种方式在业务开发中比较常见,例如:
User user = userOptional.orElseThrow(
() -> new RuntimeException("用户不存在")
);抛出的异常可以继续交给 Spring 的统一异常处理机制进行处理。
6. 过滤
6.1 filter()
Optional 也提供了 filter(),可以对内部的数据进行条件过滤。
如果:
Optional本身没有值- 或者有值但不满足条件
最终都会得到一个空的 Optional。
例如:只处理年龄大于 18 岁的作者。
完整写法:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional
.filter(new Predicate<Author>() {
@Override
public boolean test(Author author) {
return author.getAge() > 18;
}
})
.ifPresent(
author -> System.out.println(author.getName())
);
}
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return Optional.ofNullable(author);
}Lambda 简化:
Optional<Author> authorOptional = getAuthorOptional();
authorOptional
.filter(author -> author.getAge() > 18)
.ifPresent(author -> System.out.println(author.getName()));执行过程:
Optional<Author>
↓
age > 18 ?
↙ ↘
满足 不满足
↓ ↓
保留 Optional.empty()7. 判断是否存在数据
7.1 isPresent()
isPresent() 用于判断 Optional 内部是否存在数据。
Optional<Author> authorOptional = getAuthorOptional();
if (authorOptional.isPresent()) {
System.out.println(authorOptional.get().getName());
}返回结果:
有值 → true
无值 → false虽然可以这样使用,但:
if (optional.isPresent()) {
optional.get();
}本质上又回到了手动判断的写法。
如果只是有值时执行操作,通常更推荐:
authorOptional.ifPresent(
author -> System.out.println(author.getName())
);8. 数据转换
8.1 map()
Optional 提供了 map(),可以对内部的数据进行转换。
转换后的结果仍然会被 Optional 包装。
例如:
Optional<Author>
↓
map()
↓
Optional<List<Book>>完整写法:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional
.map(new Function<Author, List<Book>>() {
@Override
public List<Book> apply(Author author) {
return author.getBooks();
}
})
.ifPresent(
new Consumer<List<Book>>() {
@Override
public void accept(List<Book> books) {
System.out.println(books);
}
}
);
}
private static Optional<Author> getAuthorOptional() {
Author author = new Author(
1L,
"蒙多",
33,
"一个从菜刀中明悟哲理的祖安人",
null
);
return Optional.ofNullable(author);
}Lambda 简化:
public static void main(String[] args) {
Optional<Author> authorOptional = getAuthorOptional();
authorOptional
.map(author -> author.getBooks())
.ifPresent(books -> System.out.println(books));
}执行过程:
Optional<Author>
↓
获取 books
↓
Optional<List<Book>>
↓
ifPresent()
↓
使用 books如果原来的 Optional 没有值,map() 中的转换逻辑也不会执行。
9. 常用方法小结
| 方法 | 作用 |
|---|---|
ofNullable() | 将可能为 null 的数据包装成 Optional |
of() | 包装确定不为 null 的数据 |
empty() | 创建一个空的 Optional |
ifPresent() | 有值时执行操作 |
get() | 直接获取值,不推荐盲目使用 |
orElseGet() | 无值时通过 Supplier 提供默认值 |
orElseThrow() | 无值时抛出异常 |
filter() | 对内部数据进行过滤 |
isPresent() | 判断是否存在数据 |
map() | 转换内部的数据 |
可以简单理解为:
ofNullable()
↓
包装可能为空的数据
ifPresent()
↓
有值就执行
orElseGet()
↓
没值就给默认值
orElseThrow()
↓
没值就抛异常
filter()
↓
过滤
map()
↓
转换Optional 的核心不是“消灭 null”,而是明确表达一个值可能不存在,并提供一套更安全的方式处理这种情况。