为什么需要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 ===");
String name = getUserName(); if (name != null) { if (name.length() > 0) { System.out.println(name.toUpperCase()); } }
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 ===");
Optional<String> opt1 = Optional.of("Hello"); System.out.println(opt1);
Optional<String> opt2 = Optional.ofNullable("Hello"); Optional<String> opt3 = Optional.ofNullable(null);
Optional<String> opt4 = Optional.empty(); System.out.println(opt4); }
|
| 方法 |
说明 |
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();
System.out.println("有值?" + opt.isPresent()); System.out.println("有值?" + empty.isPresent());
opt.ifPresent(s -> System.out.println("值是:" + s)); empty.ifPresent(s -> System.out.println("不会打印"));
System.out.println(opt.get());
System.out.println(empty.orElse("默认值"));
System.out.println(empty.orElseGet(() -> "动态生成的默认值"));
}
|
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");
Optional<String> upper = opt.map(String::toUpperCase); System.out.println(upper.get());
Optional<Integer> length = opt.map(String::length); System.out.println(length.get());
String result = Optional.ofNullable(getUser()) .map(User::getAddress) .map(Address::getCity) .orElse("未知城市"); System.out.println("城市:" + result);
Optional<String> flatResult = opt.flatMap(s -> Optional.of(s.toUpperCase())); System.out.println(flatResult.get()); }
|
| 方法 |
作用 |
无值时行为 |
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("有值");
String r1 = opt.orElse(expensiveOperation());
String r2 = opt.orElseGet(() -> expensiveOperation());
}
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 错误用法 ===");
}
|
实战代码示例
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实战 ===");
String cityName = Optional.ofNullable(user) .map(User::getAddress) .map(Address::getCity) .map(City::getName) .orElse("未知");
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);
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<String> opt = findName(); String name = opt.get();
String name = findName().orElse("默认名");
|
坑3:isPresent + get 等于回到了if-null的老路
1 2 3 4 5 6 7
| if (opt.isPresent()) { System.out.println(opt.get()); }
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。正确做法是整体重新设计逻辑