Java Optional 类详解

更新于 2025-12-27

baeldung 2024-01-16

1. 概述

在本教程中,我们将介绍 Java 8 引入的 Optional 类。

该类的目的是提供一种类型级别的解决方案,用于表示可选值,而不是使用空引用(null references)。

若想更深入地理解为何我们应该关注 Optional 类,请参考 Oracle 官方文章


2. 创建 Optional 对象

有多种方式可以创建 Optional 对象。

创建空的 Optional 对象

只需调用其静态方法 empty()

@Test
public void whenCreatesEmptyOptional_thenCorrect() {
    Optional<String> empty = Optional.empty();
    assertFalse(empty.isPresent());
}

注意:我们使用了 isPresent() 方法来检查 Optional 对象中是否包含值。只有当我们使用非 null 值创建 Optional 时,才认为值存在。下一节将详细介绍 isPresent() 方法。

使用 of() 创建 Optional

@Test
public void givenNonNull_whenCreatesNonNullable_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.of(name);
    assertTrue(opt.isPresent());
}

⚠️ 注意:传递给 of() 方法的参数不能为 null,否则会抛出 NullPointerException

@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {
    String name = null;
    Optional.of(name);
}

使用 ofNullable() 处理可能为 null 的值

@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {
    String name = "baeldung";
    Optional<String> opt = Optional.ofNullable(name);
    assertTrue(opt.isPresent());
}

如果传入的是 null 引用,不会抛出异常,而是返回一个空的 Optional 对象:

@Test
public void givenNull_whenCreatesNullable_thenCorrect() {
    String name = null;
    Optional<String> opt = Optional.ofNullable(name);
    assertFalse(opt.isPresent());
}

3. 检查值是否存在:isPresent()isEmpty()

当获得一个 Optional 对象(无论是方法返回还是自行创建),我们可以使用 isPresent() 方法判断其中是否包含值:

@Test
public void givenOptional_whenIsPresentWorks_thenCorrect() {
    Optional<String> opt = Optional.of("Baeldung");
    assertTrue(opt.isPresent());

    opt = Optional.ofNullable(null);
    assertFalse(opt.isPresent());
}

该方法在包装的值不为 null 时返回 true

Java 11 开始,还可以使用相反的 isEmpty() 方法:

@Test
public void givenAnEmptyOptional_thenIsEmptyBehavesAsExpected() {
    Optional<String> opt = Optional.of("Baeldung");
    assertFalse(opt.isEmpty());

    opt = Optional.ofNullable(null);
    assertTrue(opt.isEmpty());
}

4. 条件执行:ifPresent()

ifPresent() 方法允许我们在值非 null 时执行某些代码。

在没有 Optional 之前,我们通常这样写:

if(name != null) {
    System.out.println(name.length());
}

这种方式不仅冗长,还容易出错——比如后续再次使用 name 时忘记做 null 检查,可能导致运行时 NullPointerException

Optional 强制我们显式处理可空值,从而促进良好的编程实践。

使用 Java 8 的函数式风格,我们可以这样重构:

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
    opt.ifPresent(name -> System.out.println(name.length()));
}

仅用两行代码就替代了之前的五行业务逻辑:一行包装对象,一行隐式验证并执行操作。


5. 提供默认值:orElse()

orElse() 方法用于获取 Optional 中包装的值。它接受一个参数作为默认值:

  • 如果值存在,返回该值;
  • 否则返回传入的默认值。
@Test
public void whenOrElseWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElse("john");
    assertEquals("john", name);
}

6. 提供默认值:orElseGet()

orElseGet()orElse() 类似,但接收一个 Supplier 函数式接口,仅在需要时才调用:

@Test
public void whenOrElseGetWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
    assertEquals("john", name);
}

7. orElse()orElseGet() 的区别

对很多初学者而言,这两个方法看似功能重叠,但实际上存在关键性能差异

定义一个方法用于测试:

public String getMyDefault() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

情况一:值为 null(两者行为一致)

@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
    String text = null;
    String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Default Value", defaultText);

    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Default Value", defaultText);
}

输出:

Getting Default Value
Getting Default Value

两个方法都调用了 getMyDefault()

情况二:值存在(行为不同!)

@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
    String text = "Text present";

    System.out.println("Using orElseGet:");
    String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
    assertEquals("Text present", defaultText);

    System.out.println("Using orElse:");
    defaultText = Optional.ofNullable(text).orElse(getMyDefault());
    assertEquals("Text present", defaultText);
}

输出:

Using orElseGet:
Using orElse:
Getting default value...
  • orElseGet()未调用 getMyDefault(),因为值已存在。
  • orElse()仍然调用getMyDefault(),即使结果未被使用!

💡 结论:若默认值的生成成本高(如数据库查询、网络请求),应优先使用 orElseGet() 以避免不必要的开销。


8. 抛出异常:orElseThrow()

当值不存在时,orElseThrow() 不返回默认值,而是抛出异常

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow(IllegalArgumentException::new);
}

Java 10 引入了无参版本,此时抛出 NoSuchElementException

@Test(expected = NoSuchElementException.class)
public void whenNoArgOrElseThrowWorks_thenCorrect() {
    String nullName = null;
    String name = Optional.ofNullable(nullName).orElseThrow();
}

9. 直接获取值:get()

get() 是最直接的取值方式:

@Test
public void givenOptional_whenGetsValue_thenCorrect() {
    Optional<String> opt = Optional.of("baeldung");
    String name = opt.get();
    assertEquals("baeldung", name);
}

⚠️ 但若值为 null,会抛出 NoSuchElementException

@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {
    Optional<String> opt = Optional.ofNullable(null);
    String name = opt.get();
}

重要提示get() 违背了 Optional 避免空指针异常的初衷,不推荐使用,未来版本可能被弃用。建议使用 orElse()orElseGet() 等安全方法。


10. 条件过滤:filter()

filter() 接收一个谓词(Predicate),若值满足条件则返回原 Optional,否则返回空 Optional

@Test
public void whenOptionalFilterWorks_thenCorrect() {
    Integer year = 2016;
    Optional<Integer> yearOptional = Optional.of(year);
    boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent();
    assertTrue(is2016);
    boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent();
    assertFalse(is2017);
}

实际应用示例:检查调制解调器价格是否在预算内

传统写法(冗长且易漏检)

public boolean priceIsInRange1(Modem modem) {
    boolean isInRange = false;
    if (modem != null && modem.getPrice() != null 
        && (modem.getPrice() >= 10 && modem.getPrice() <= 15)) {
        isInRange = true;
    }
    return isInRange;
}

使用 Optional + filter(简洁且安全)

public boolean priceIsInRange2(Modem modem) {
    return Optional.ofNullable(modem)
        .map(Modem::getPrice)
        .filter(p -> p >= 10)
        .filter(p -> p <= 15)
        .isPresent();
}

✅ 优势:

  • 自动处理 null;
  • 逻辑清晰,只关注核心业务(价格区间判断);
  • 链式调用,可读性强。

11. 转换值:map()

map() 用于对 Optional 中的值进行转换:

@Test
public void givenOptional_whenMapWorks_thenCorrect() {
    List<String> companyNames = Arrays.asList("paypal", "oracle", "", "microsoft", "", "apple");
    Optional<List<String>> listOptional = Optional.of(companyNames);

    int size = listOptional.map(List::size).orElse(0);
    assertEquals(6, size);
}

另一个例子:

@Test
public void givenOptional_whenMapWorks_thenCorrect2() {
    String name = "baeldung";
    Optional<String> nameOptional = Optional.of(name);
    int len = nameOptional.map(String::length).orElse(0);
    assertEquals(8, len);
}

结合 map()filter():清理并验证密码

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {
    String password = " password ";
    Optional<String> passOpt = Optional.of(password);

    // 未清理:失败
    boolean correctPassword = passOpt.filter(pass -> pass.equals("password")).isPresent();
    assertFalse(correctPassword);

    // 清理后:成功
    correctPassword = passOpt
        .map(String::trim)
        .filter(pass -> pass.equals("password"))
        .isPresent();
    assertTrue(correctPassword);
}

12. 扁平化转换:flatMap()

map() 不同,flatMap() 用于处理返回 Optional 的函数,避免嵌套 Optional

假设有一个 Person 类,其 getter 返回 Optional

public class Person {
    private String name;
    // ...
    public Optional<String> getName() {
        return Optional.ofNullable(name);
    }
}

使用 map() 会导致 Optional<Optional<String>>

Optional<Person> personOptional = Optional.of(new Person("john", 26));
Optional<Optional<String>> nameOptionalWrapper = personOptional.map(Person::getName);

flatMap() 会自动“解包”:

String name = personOptional
    .flatMap(Person::getName)
    .orElse("");
assertEquals("john", name);

规则:当转换函数返回 Optional 时,使用 flatMap();否则使用 map()


13. 在 Java 8 中链式处理多个 Optional

Java 8 没有直接支持“返回第一个非空 Optional”的方法,但可通过 Stream 实现:

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional<String> found = Stream.of(getEmpty(), getHello(), getBye())
        .filter(Optional::isPresent)
        .map(Optional::get)
        .findFirst();
    assertEquals(getHello(), found);
}

⚠️ 问题:所有方法都会被立即执行。

懒加载版本(推荐)

@Test
public void givenThreeOptionals_whenChaining_thenFirstNonEmptyIsReturnedAndRestNotEvaluated() {
    Optional<String> found = Stream.<Supplier<Optional<String>>>of(this::getEmpty, this::getHello, this::getBye)
        .map(Supplier::get)
        .filter(Optional::isPresent)
        .map(Optional::get)
        .findFirst();
    assertEquals(getHello(), found);
}

支持带参方法(使用 Lambda):

@Test
public void givenTwoOptionalsReturnedByOneArgMethod_whenChaining_thenFirstNonEmptyIsReturned() {
    Optional<String> found = Stream.<Supplier<Optional<String>>>of(
        () -> createOptional("empty"),
        () -> createOptional("hello")
    )
    .map(Supplier::get)
    .filter(Optional::isPresent)
    .map(Optional::get)
    .findFirst();
    assertEquals(createOptional("hello"), found);
}

提供默认值

String found = ... .findFirst().orElseGet(() -> "default");

14. JDK 9 新增的 Optional 方法

14.1 or() 方法

返回另一个 Optional(懒加载):

@Test
public void givenOptional_whenPresent_thenShouldTakeAValueFromIt() {
    Optional<String> value = Optional.of("properValue");
    Optional<String> result = value.or(() -> Optional.of("default"));
    assertThat(result.get()).isEqualTo("properValue");
}

@Test
public void givenOptional_whenEmpty_thenShouldTakeAValueFromOr() {
    Optional<String> value = Optional.empty();
    Optional<String> result = value.or(() -> Optional.of("default"));
    assertThat(result.get()).isEqualTo("default");
}

14.2 ifPresentOrElse() 方法

值存在时执行一个操作,否则执行另一个:

@Test
public void givenOptional_whenPresent_thenShouldExecuteProperCallback() {
    Optional<String> value = Optional.of("properValue");
    AtomicInteger successCounter = new AtomicInteger(0);
    AtomicInteger onEmptyOptionalCounter = new AtomicInteger(0);

    value.ifPresentOrElse(
        v -> successCounter.incrementAndGet(),
        onEmptyOptionalCounter::incrementAndGet
    );

    assertThat(successCounter.get()).isEqualTo(1);
    assertThat(onEmptyOptionalCounter.get()).isEqualTo(0);
}

14.3 stream() 方法

Optional 转为 Stream

  • 有值 → 单元素 Stream;
  • 无值 → 空 Stream。
@Test
public void givenOptionalOfSome_whenToStream_thenShouldTreatItAsOneElementStream() {
    Optional<String> value = Optional.of("a");
    List<String> collect = value.stream().map(String::toUpperCase).collect(Collectors.toList());
    assertThat(collect).hasSameElementsAs(List.of("A"));
}

@Test
public void givenOptionalOfNone_whenToStream_thenShouldTreatItAsZeroElementStream() {
    Optional<String> value = Optional.empty();
    List<String> collect = value.stream().map(String::toUpperCase).collect(Collectors.toList());
    assertThat(collect).isEmpty();
}

15. Optional 的误用:不要作为方法参数

错误示例

public static List<Person> search(List<Person> people, String name, Optional<Integer> age) {
    return people.stream()
        .filter(p -> p.getName().equals(name))
        .filter(p -> p.getAge().get() >= age.orElse(0))
        .collect(Collectors.toList());
}

调用时若传 null

someObject.search(people, "Peter", null); // NullPointerException!

正确做法

方案一:直接使用原始类型 + null 检查

public static List<Person> search(List<Person> people, String name, Integer age) {
    final Integer ageFilter = age != null ? age : 0;
    return people.stream()
        .filter(p -> p.getName().equals(name))
        .filter(p -> p.getAge().get() >= ageFilter)
        .collect(Collectors.toList());
}

方案二:方法重载

public static List<Person> search(List<Person> people, String name) {
    return doSearch(people, name, 0);
}

public static List<Person> search(List<Person> people, String name, int age) {
    return doSearch(people, name, age);
}

📌 原则Optional仅作为返回类型不应作为方法参数或字段类型


16. Optional 与序列化

  • Optional 不是 Serializable,在可序列化类中使用会导致 NotSerializableException
  • 不建议将 Optional 用作字段类型。
  • 如需与 Jackson 等框架集成,需特殊处理(如自定义序列化器)。

17. 总结

本文全面介绍了 Java 8 Optional 类的核心特性:

  • 使用 of(), ofNullable(), empty() 创建 Optional
  • 通过 isPresent(), isEmpty() 检查值是否存在;
  • 使用 ifPresent() 安全执行操作;
  • 通过 orElse(), orElseGet(), orElseThrow() 提供默认值或异常;
  • 利用 filter(), map(), flatMap() 进行条件过滤与值转换;
  • Java 9 新增 or(), ifPresentOrElse(), stream() 增强功能;
  • 避免将 Optional 用作方法参数或字段
  • Optional 的设计初衷是作为返回类型,提升 API 的表达力与安全性。

合理使用 Optional,可显著减少空指针异常,写出更健壮、更清晰的代码。