空检查与可选的 isPresent 检查

Null check vs Optional isPresent check

谁能解释一下 Optional 如何帮助我们避免 NullPointerException

Optional<String> op = someFunc()
if(op.isPresent()) {
   op.get();
}
String possibleNull = op.get();

这段代码是不是也容易NullPointerException?如果是这样,那么为什么此代码优于

String op = someFunc()
if(op != null) {
   op.get();
}
String possibleNull = op;

Optional 除了帮助我们了解一个函数是否确实具有 return 值之外,还有什么可能的好处

假设您要获取函数返回的字符串,将其转换为大写,然后打印出来。如果你有:

String someFunc() { ... }

您可能会想写:

System.out.println(someFunc().toUpperCase());

当然,如果someFunc returns null,这会抛出NullPointerException。相反,假设我们有这个:

Optional<String> someFunc() { ... }

然后

System.out.println(someFunc().toUpperCase());

不会工作,因为 Optional 没有 toUpperCase 方法。此时——希望——你会遇到一个 Optional,这应该让你考虑 Optional 为空的情况。这有助于避免 NPE,但可能只是一点点。

现在您可能专注于如何从 Optional 中获取值,而您可能会忘记空 case。啊,有个get方法:

System.out.println(someFunc().get().toUpperCase());

这又带来了与 NPE 相同的问题,除了例外是 NoSuchElementException。因此,如果您盲目地在 Optional 上调用 get,这实际上与在不检查引用是否为 null 的情况下调用方法几乎是一回事。

(因此,Brian Goetz considers Optional.get to be the biggest mistake in Java 8. See his interview with Angelika Langer JAX 2015 Fragen und Antworten zu Java 8 在大约 16 分钟内。我不确定它是不是最大的,但这是一个错误。人们只是不希望 get 抛出一个例外。)

如果您勤于检查空引用或空可选值,那么

Optional<String> os = someFunc();
if (os.isPresent()) {
    System.out.println(os.get().toUpperCase());
}

几乎不比旧的好多少

String s = someFunc();
if (s != null) {
    System.out.println(s.toUpperCase());
}

真正 Optional 的优点是它是一个库 class 具有相当丰富的 API 处理空案例以安全的方式。通常可以通过将几个方法调用链接到首先返回 Optional 的方法来处理可能包含在 Optional 中的值。例如,我们可以将上面的示例重写为:

someFunc().map(String::toUpperCase)
          .ifPresent(System.out::println);
String op = someFunc()
if(op != null) {
   op.trim();
}

上面调用接口 someFunc() 时,它没有明确说明可以 returned 空值,因此调用者 his/her 自己假设。

通过显式 return 一个 Optional,someFunc() 的调用者知道该接口可能 return null。从界面创建者的角度来看,它让 him/her 可以具体说明 return 值,而不必单独记录它。

Optional<String> op = someFunc()
if(op.isPresent()) {
   op.get().trim();
}