1. 概述
本文将探讨在使用 findFirst()
方法时避免 NullPointerException
的几种方案。首先说明该方法抛出异常的原因,然后通过实际示例演示如何复现和修复这个问题。
2. 问题解析
简单来说,NullPointerException
是在需要对象却使用了 null
时抛出的异常。通常我们用 findFirst()
获取流的首个元素并包装成 Optional
,但根据 官方文档,**当首个元素为 null
时会直接抛出 NullPointerException
**。
核心问题在于:当流的首元素是 null
时如何避免异常?在深入解决方案前,先复现这个问题。
3. 复现 NullPointerException
假设有一个包含 null
的字符串列表:
List<String> inputs = Arrays.asList(null, "foo", "bar");
尝试用 findFirst()
获取首元素:
@Test(expected = NullPointerException.class)
public void givenStream_whenCallingFindFirst_thenThrowNullPointerException() {
Optional<String> firstElement = inputs.stream()
.findFirst();
}
测试会抛出 NullPointerException
,因为列表首元素是 null
。
⚠️ 关键点:Optional
API 要求调用者确保值非 null
,因为它无法区分“值为 null
”和“值不存在”两种情况。这就是为什么 findFirst()
禁止返回 null
。
4. 避免异常的方案
方案一:使用 filter 过滤 null
最简单粗暴的方法是在调用 findFirst()
前先过滤掉 null
:
@Test
public void givenStream_whenUsingFilterBeforeFindFirst_thenCorrect() {
Optional<String> firstNotNullElement = inputs.stream()
.filter(Objects::nonNull)
.findFirst();
assertTrue(firstNotNullElement.isPresent());
}
通过 Objects::nonNull
过滤后,确保首个元素非 null
,从而避免异常。✅
方案二:使用 Optional.ofNullable 包装
另一种方案是先用 Optional.ofNullable
包装元素:
@Test
public void givenStream_whenUsingOfNullableBeforeFindFirst_thenCorrect() {
Optional<String> firstElement = inputs.stream()
.map(Optional::ofNullable)
.findFirst()
.flatMap(Function.identity());
assertTrue(firstElement.isEmpty());
}
操作步骤:
- 用
ofNullable()
将每个元素转为Optional
(支持null
) findFirst()
返回Optional<Optional<String>>
- 用
flatMap()
展平嵌套的Optional
注意:Function.identity()
会直接返回输入参数,这里返回 null
因为它是列表首元素。最终得到空 Optional
。✅
5. 总结
本文介绍了处理 findFirst()
遇到 null
元素时的两种实用方案:
- 过滤法:用
filter(Objects::nonNull)
提前排除null
- 包装法:用
Optional.ofNullable
+flatMap
安全处理null
完整示例代码可在 GitHub 获取。