Java8 Optional类初体验

在现在的公司实习已经一个多月了,在每天的做任务改八哥中,要说给我印象最深,用了以后觉得最爽的一个新知识,就非Optional莫属了。(于是上班偷偷摸鱼整理了这篇文章?)

Optional是Java8引入的一个很有趣的特性,用来解决最经常遇到的烦人问题: NullPointerException(空指针异常)。本质上,Optional是一个包装类,支持泛型地将对象保存在Optional类中,并提供许多有意思的方法来调用和处理存储的对象为空时的异常。

什么?如果调用的Option对象为空?放心啦,你不会去手动new一个Optional对象的,Optional是一个final类,唯二的构造方法都是private的,你需要通过Optional.of()等static方法来获取一个新对象,所以并不会收到一个null对象哦。

接下来跟着代码一步步接近Optional,感受其中的奥秘吧。

一. 不使用 Optional 的情况

  1. 常规的调用逻辑
1
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
  1. 常规的 null 处理
1
2
3
4
5
6
7
8
9
10
11
12
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}

二. Optional 对象的创建

  1. empty(): 尝试访问 emptyOpt 变量的值会导致 NoSuchElementException。

    1
    2
    Optional<User> emptyOpt = Optional.empty();
    emptyOpt.get();
  2. of(): 如果user为空,会抛出NullPointerException。

    1
    Optional<User> opt = Optional.of(user);
  3. ofNullable(): 如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法。

    1
    Optional<User> opt = Optional.ofNullable(user);

三. Optional 值的访问

  1. get(): 如果opt为空,会抛出NullPointerException。

    1
    2
    3
    String name = "John";
    Optional<String> opt = Optional.ofNullable(name);
    assertEquals("John", opt.get());
  2. isPresent(): 检查对象是否存在,null 时为 false。

  3. ifPresent(): 只有 user 用户不为 null 的时候才会执行断言。

    1
    opt.ifPresent( u -> assertEquals(user.getEmail(), u.getEmail()));

四. Optional 空值的处理

  1. orElse(): 如果user为空,返回user2,否则返回user。

    1
    2
    3
    4
    5
    User user = null;
    User user2 = new User("[email protected]", "1234");
    User result = Optional.ofNullable(user).orElse(user2);

    assertEquals(user2.getEmail(), result.getEmail());
  2. orElseGet(): 如果user为空,执行Supplier(供应者) 函数式接口,并将返回其执行结果。

    1
    User result = Optional.ofNullable(user).orElseGet( () -> user2);
  3. orElse() 和 orElseGet() 的不同之处

    1. 当对象为空而返回默认对象时,行为并无差异。
    2. 当对象非空值时,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象,orElseGet() 方法不创建 User 对象。
  4. orElseThrow(): 在对象为空的时候抛出异常,而不是返回备选的值。

    1
    2
    User result = Optional.ofNullable(user)
    .orElseThrow( () -> new IllegalArgumentException());

五. Optional 值的转换和过滤

  1. map(): 将值作为参数调用函数,然后将返回的值包装在 Optional 中。

    1
    2
    3
    4
    5
    User user = new User("[email protected]", "1234");
    String email = Optional.ofNullable(user)
    .map(u -> u.getEmail()).orElse("[email protected]");
    // 上面map()后是 Optional 对象,所以可以链式调用orElse()。
    assertEquals(email, user.getEmail());
  2. flatMap(): 过程与map()一致,但直接返回结果,而非包装为 Optional。

  3. 类方法返回 Optional 对象

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    // 类的定义
    private class User {
    private String position;

    public Optional<String> getPosition() {
    return Optional.ofNullable(position);
    }

    //...
    }

    // 调用示例
    public void whenFlatMap_thenOk() {
    User user = new User("[email protected]", "1234");
    user.setPosition("Developer");
    String position = Optional.ofNullable(user)
    .flatMap(u -> u.getPosition()).orElse("default");

    assertEquals(position, user.getPosition().get());
    }
  4. filter(): 接受一个 Predicate 参数,true 返回值,false返回空 Optional。

    1
    2
    3
    4
    5
    User user = new User("[email protected]", "1234");
    Optional<User> result = Optional.ofNullable(user)
    .filter(u -> u.getEmail() != null && u.getEmail().contains("@"));

    assertTrue(result.isPresent());

六. Optional 类的链式方法演示

  1. 嵌套类,getter返回 Optional 对象

    About-Java8-Optional-First-Experience_001

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class User {
    private Address address;

    public Optional<Address> getAddress() {
    return Optional.ofNullable(address);
    }

    // ...
    }
    public class Address {
    private Country country;

    public Optional<Country> getCountry() {
    return Optional.ofNullable(country);
    }

    // ...
    }
  2. 测试方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    User user = new User("[email protected]", "1234");

    // 因为该自定义类返回的值本身就是 Optional 对象,所以用flatMap();
    String result = Optional.ofNullable(user)
    .flatMap(u -> u.getAddress())
    .flatMap(a -> a.getCountry())
    .map(c -> c.getIsocode())
    .orElse("default");

    assertEquals(result, "default");
  3. 简化测试方法(Lambda)

    1
    2
    3
    4
    5
    String result = Optional.ofNullable(user)
    .flatMap(User::getAddress)
    .flatMap(Address::getCountry)
    .map(Country::getIsocode)
    .orElse("default")

七. 参考来源

更多信息以及关于Java9对Optional的增强,可以参考:


Java8 Optional类初体验
https://tech.initialize.in/old_before_2021/About-Java8-Optional-First-Experience/
作者
Emmett Woo
发布于
2021年1月24日
许可协议