Optional类

为什么需要Optional

一句话理解:Optional就是一个可能装了东西也可能是空的盒子,强迫你在拆盒子之前想清楚”如果是空的怎么办”

Java里最常见的异常就是 NullPointerException(简称NPE),Optional就是用来优雅地处理null的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testWhyOptional() {
System.out.println("=== 为什么需要Optional ===");

// 以前的写法:到处判null,写到怀疑人生
String name = getUserName(); // 可能返回null
if (name != null) {
if (name.length() > 0) {
System.out.println(name.toUpperCase());
}
}

// 用Optional的写法:链式调用,清清爽爽
Optional<String> optName = Optional.ofNullable(getUserName());
optName.filter(n -> n.length() > 0)
.map(String::toUpperCase)
.ifPresent(System.out::println);
}

创建Optional

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Test
public void testCreateOptional() {
System.out.println("=== 创建Optional ===");

// 1. Optional.of(值) —— 值不能为null,否则立刻抛NullPointerException
Optional<String> opt1 = Optional.of("Hello");
System.out.println(opt1); // Optional[Hello]

// Optional.of(null); // ❌ 直接抛 NullPointerException!

// 2. Optional.ofNullable(值) —— 值可以为null(最常用)
Optional<String> opt2 = Optional.ofNullable("Hello"); // Optional[Hello]
Optional<String> opt3 = Optional.ofNullable(null); // Optional.empty

// 3. Optional.empty() —— 创建一个空的Optional
Optional<String> opt4 = Optional.empty();
System.out.println(opt4); // Optional.empty
}
方法 说明 null值
Optional.of(value) 包装非null值 传null直接抛异常
Optional.ofNullable(value) 包装可能为null的值 传null返回empty
Optional.empty() 创建空Optional -

实战建议:不确定是不是null就用 ofNullable,别冒险用 of

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void testOptionalMethods() {
System.out.println("=== Optional常用方法 ===");

Optional<String> opt = Optional.of("Hello World");
Optional<String> empty = Optional.empty();

// isPresent:判断有没有值
System.out.println("有值?" + opt.isPresent()); // true
System.out.println("有值?" + empty.isPresent()); // false

// isEmpty (Java 11+):判断是不是空的
// System.out.println("空的?" + empty.isEmpty()); // true

// ifPresent:有值就执行,没值就跳过
opt.ifPresent(s -> System.out.println("值是:" + s)); // 值是:Hello World
empty.ifPresent(s -> System.out.println("不会打印")); // 不执行

// get:直接取值(⚠️ 空的话会抛 NoSuchElementException,尽量别用!)
System.out.println(opt.get()); // Hello World
// empty.get(); // ❌ 抛异常!

// orElse:有值返回值,没值返回默认值
System.out.println(empty.orElse("默认值")); // 默认值

// orElseGet:有值返回值,没值用Supplier生成默认值(懒加载)
System.out.println(empty.orElseGet(() -> "动态生成的默认值"));

// orElseThrow:有值返回值,没值抛异常
// empty.orElseThrow(() -> new RuntimeException("值不能为空!"));
}

map 和 flatMap:链式转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Test
public void testOptionalMapFlatMap() {
System.out.println("=== map 和 flatMap ===");

Optional<String> opt = Optional.of("hello world");

// map:对值做转换
Optional<String> upper = opt.map(String::toUpperCase);
System.out.println(upper.get()); // HELLO WORLD

Optional<Integer> length = opt.map(String::length);
System.out.println(length.get()); // 11

// 链式调用:安全地一层层剥
String result = Optional.ofNullable(getUser())
.map(User::getAddress)
.map(Address::getCity)
.orElse("未知城市");
System.out.println("城市:" + result);

// flatMap:当转换函数本身返回Optional时使用
// map 会把结果包成 Optional<Optional<String>>(套娃了)
// flatMap 会把结果"拍平"成 Optional<String>
Optional<String> flatResult = opt.flatMap(s -> Optional.of(s.toUpperCase()));
System.out.println(flatResult.get()); // HELLO WORLD
}
方法 作用 无值时行为
isPresent() 判断有没有值 返回false
ifPresent(Consumer) 有值就消费 什么都不做
get() 直接取值 抛NoSuchElementException
orElse(默认值) 取值或返回默认值 返回默认值
orElseGet(Supplier) 取值或懒生成默认值 执行Supplier
orElseThrow(Supplier) 取值或抛异常 抛指定异常
map(Function) 转换值 返回empty
flatMap(Function) 转换值(拍平) 返回empty
filter(Predicate) 过滤值 返回empty

orElse vs orElseGet 的区别

看起来差不多,但有个重要区别:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Test
public void testOrElseVsOrElseGet() {
System.out.println("=== orElse vs orElseGet ===");

Optional<String> opt = Optional.of("有值");

// orElse:不管有没有值,默认值的表达式都会执行
String r1 = opt.orElse(expensiveOperation()); // expensiveOperation() 会被调用!

// orElseGet:只有没值的时候,Supplier才会执行
String r2 = opt.orElseGet(() -> expensiveOperation()); // 不会被调用

// 结论:如果默认值的创建成本很高(比如查数据库),用 orElseGet
}

private String expensiveOperation() {
System.out.println("执行了耗时操作!");
return "默认值";
}

简单记:orElse 传的是值(总会计算),orElseGet 传的是函数(按需计算)

不要滥用Optional

Optional是为了方法返回值设计的,不是万能药

正确用法 vs 错误用法

场景 推荐做法 说明
方法返回值可能为null ✅ 返回 Optional<T> 这是Optional的本职工作
方法参数 ❌ 别用Optional做参数 直接传值,null就判断
类的字段 ❌ 别用Optional做字段 Optional不能序列化
集合返回值 ❌ 别用 Optional<List> 返回空集合就好
简单的null判断 ❌ 别为了用而用 if (x != null) 有时更清晰
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Test
public void testOptionalUsage() {
System.out.println("=== 正确用法 vs 错误用法 ===");

// ✅ 正确:方法返回Optional
// public Optional<User> findUserById(Long id) {
// return Optional.ofNullable(userMap.get(id));
// }

// ❌ 错误:Optional做参数(调用者还得包装一层,多此一举)
// public void process(Optional<String> name) { ... }
// 应该写成:
// public void process(String name) { if (name != null) ... }

// ❌ 错误:Optional做字段
// class User {
// private Optional<String> nickname; // 别这样!
// }
// 应该写成:
// class User {
// private String nickname; // 用的时候再判null
// }

// ❌ 错误:用Optional包装集合
// public Optional<List<User>> getUsers() { ... }
// 应该写成:
// public List<User> getUsers() { return Collections.emptyList(); }

// ❌ 错误:用Optional只是为了调get
// String value = Optional.ofNullable(str).get(); // 这不是脱裤子放屁吗
}

实战代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Test
public void testOptionalPractice() {
System.out.println("=== Optional实战 ===");

// 场景1:安全地获取嵌套属性
// User -> Address -> City -> Name
String cityName = Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.map(City::getName)
.orElse("未知");

// 场景2:配合Stream使用
List<String> names = Arrays.asList("Alice", null, "Bob", null, "Charlie");
List<String> validNames = names.stream()
.map(Optional::ofNullable)
.filter(Optional::isPresent)
.map(Optional::get)
.collect(Collectors.toList());
// 上面可以简写为:
List<String> validNames2 = names.stream()
.filter(Objects::nonNull)
.collect(Collectors.toList());
System.out.println("有效名字:" + validNames2); // [Alice, Bob, Charlie]

// 场景3:给默认值并做转换
int length = Optional.ofNullable(getUserName())
.map(String::trim)
.filter(s -> !s.isEmpty())
.map(String::length)
.orElse(0);
System.out.println("名字长度:" + length);
}

更多关于 [Stream API](/2026/04/04/Stream API/) 配合Optional的用法可以参考Stream笔记

常见坑

坑1:Optional.of(null) 直接炸

不确定是不是null,永远用 Optional.ofNullable()

坑2:直接调get()不判断,跟没用Optional一样

1
2
3
4
5
6
// ❌ 这样写Optional白用了
Optional<String> opt = findName();
String name = opt.get(); // 如果是empty,照样抛异常

// ✅ 用orElse或ifPresent
String name = findName().orElse("默认名");

坑3:isPresent + get 等于回到了if-null的老路

1
2
3
4
5
6
7
// ❌ 这不就是另一种写法的 if (x != null) 吗
if (opt.isPresent()) {
System.out.println(opt.get());
}

// ✅ 直接用 ifPresent
opt.ifPresent(System.out::println);

坑4:Optional不能序列化

所以别用在实体类字段上,Jackson/Gson序列化会出问题

练习题

题1:写一个方法 Optional<String> findLongestString(List<String> list),找到列表中最长的字符串,列表为空或null时返回 Optional.empty()

题2:用Optional链式调用实现:从Map中取值,如果值存在且长度大于3,转成大写返回;否则返回”N/A”

题3:下面代码有什么问题?怎么改?

1
2
3
4
5
public String getDisplayName(User user) {
return Optional.ofNullable(user)
.map(User::getNickname)
.orElse(user.getRealName()); // 问题在哪?
}

答案:如果 user 是null,orElse 里的 user.getRealName() 会立刻执行并抛NPE。应该用 orElseGet(() -> user.getRealName()) ——但这也不对,因为user是null。正确做法是整体重新设计逻辑


上一章 目录 下一章
[Stream API](/2026/04/04/Stream API/) java基础 方法引用与函数式接口