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());
}

操作步骤:

  1. ofNullable() 将每个元素转为 Optional(支持 null
  2. findFirst() 返回 Optional<Optional<String>>
  3. flatMap() 展平嵌套的 Optional

注意:Function.identity() 会直接返回输入参数,这里返回 null 因为它是列表首元素。最终得到空 Optional。✅

5. 总结

本文介绍了处理 findFirst() 遇到 null 元素时的两种实用方案:

  • 过滤法:用 filter(Objects::nonNull) 提前排除 null
  • 包装法:用 Optional.ofNullable + flatMap 安全处理 null

完整示例代码可在 GitHub 获取。


原始标题:Handling NullPointerException in findFirst() When the First Element Is Null | Baeldung