你应该知道的Java8新特性之Optional类

70 阅读4分钟

“我正在参加「掘金·启航计划」”

Optional类

Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,这意味着 Optional 类既可以含有对象也可以为空。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
在 Java 8 之前,任何访问对象方法或属性的调用都可能导致 NullPointerException,比如下面这段代码

//获取员工所在部门的编号
String deptno = employ.getDept().getDeptno;

如果要确保上面的代码不触发异常,就得在访问每一个值之前对其进行明确地检查:

String deptno;
if(employ != null){
    Dept dept = employ.getDept();
    if (dept != null){
        deptno = dept.getDeptno;
    }
}

虽然这样写可以避免空指针,但是代码变得冗长,难以维护。下面看看Optional是如何解决这些问题的。

Optional常用方法

  • Optional.of(T t) : 创建一个 Optional 实例
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
  • isPresent() : 判断是否包含值
  • orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
  • orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
  • map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
  • flatMap(Function mapper):与 map 类似,要求返回值必须是Optional

创建 Optional 实例

重申一下,这个类型的对象可能包含值,也可能为空。你可以使用同名方法创建一个空的 Optional。

@Test
public void createEmptyOptional() {
    Optional<User> emptyUser = Optional.empty();
    emptyUser.get();
}

//其运行结果为
Optional.empty
java.util.NoSuchElementException: No value present

像这样尝试访问 emptyUser 变量的值会导致 NoSuchElementException。
你可以使用 of() 和 ofNullable() 方法创建包含值的 Optional。两个方法的不同之处在于如果你把 null 值作为参数传递进去,of() 方法会抛出 NullPointerException:

@Test
public void createOfOrOfNullableEmptyOptional() {
    User user = null;
    Optional<User> optionalUser = Optional.ofNullable(user);
    System.out.println(optionalUser);
    System.out.println("------------------------");
    //会抛异常
    Optional<User> opt = Optional.of(user);
}

由此看来,我们并没有完全摆脱 NullPointerException。因此,在使用时,对象不为 null 的时候使用 of()。如果对象即可能是 null 也可能是非 null,你就应该使用 ofNullable() 方法;

访问 Optional 对象的值

从 Optional 实例中取回实际值对象的方法之一是使用 get() 方法:

@Test
public void createOptional() {
    String numStr = "1234";
    Optional<String> opt = Optional.ofNullable(numStr);
    assertEquals("1234", opt.get());
}

但是这个方法会在值为 null 的时候抛出异常。要避免异常,你可以选择首先验证是否有值:

@Test
public void whenCheckIfPresent_thenOk() {
    String numStr = "1234";
    Optional<String> opt = Optional.ofNullable(numStr);
    assertTrue(opt.isPresent());
    assertEquals("1234", opt.get());
}

检查是否有值的另一个选择是 ifPresent() 方法。该方法除了执行检查,还接受一个Consumer(消费者) 参数,如果对象不是空的,就对执行传入的 Lambda 表达式:

opt.ifPresent( opt -> assertEquals("1234", opt.get());

这个例子中,只有 user 用户不为 null 的时候才会执行断言。接下来,我们来看看提供空值的方法。Optional 类提供了 API 用以返回对象值,或者在对象为空的时候返回默认值。
这里可以使用 orElse(),它的工作方式非常直接,如果有值则返回该值,否则返回传递给它的参数值:

@Test
public void whenEmptyValue_thenReturnDefault() {
    User user = null;
    User user2 = new User("jack", 12);
    User result = Optional.ofNullable(user).orElse(user2);

    assertEquals(user2.getEmail(), result.getEmail());
}

这里 user 对象是空的,所以返回了作为默认值的 user2。
如果对象的初始值不是 null,那么默认值会被忽略:

@Test
public void whenValueNotNull_thenIgnoreDefault() {
    User user = new User("john@gmail.com","1234");
    User user2 = new User("anna@gmail.com", "1234");
    User result = Optional.ofNullable(user).orElse(user2);
    assertEquals("john@gmail.com", result.getEmail());
}

第二个功能类似的 API 是 orElseGet() —— 其行为略有不同。这个方法会在有值的时候返回值,如果没有值,它会执行作为参数传入的 Supplier(供应者) 函数式接口,并将返回其执行结果:

User result = Optional.ofNullable(user).orElseGet( () -> user2);

orElse() 和 orElseGet() 的不同之处在于:两个 Optional 对象都包含非空值,两个方法都会返回对应的非空值。不过,orElse() 方法仍然创建了 User 对象。与之相反,orElseGet() 方法不创建 User 对象。关于Optional的更多信息还是得查看官网API。