Java8使用Optional避免空检查
1. 认识空指针
空指针错误众所周知。Tony Hoare,空指针的发明人在2009年道歉,称之为 数十亿美元的错误。原文如下:
I call it my billion-dollarmistake. It was the invention of the null reference in 1965. At that time, Iwas designing the first comprehensive type system for references in an objectoriented language (ALGOL W). My goal was to ensure that all use of referencesshould be absolutely safe, with checking performed automatically by thecompiler. But I couldn’t resist the temptation to put in a null reference,simply because it was so easy to implement. This has led to innumerable errors,vulnerabilities, and system crashes, which have probably caused a billiondollars of pain and damage in the last forty years.
我们如何避免空指针错误,显然的答案是到处使用空检查,因为空检查麻烦且痛苦,很多语言添加了简易的语法实现,如Groovy 和Kotlin。下面是Groovy语法示例。
displayName = user.name ? user.name: 'Anonymous'
displayName = user.name ?: 'Anonymous'
2. java8空指针处理
让人失望的是java没有这样的语法,但java8有专门的Optional来实现,示例解释说明。假设我们有下面的类:
class Outer {
Nested nested;
Nested getNested() {
return nested;
}
}
class Nested {
Inner inner;
Inner getInner() {
return inner;
}
}
class Inner {
String foo;
String getFoo() {
returnfoo;
}
}
使用原来java空检查方式,需要深度嵌套,代码如下:
Outer outer = new Outer();
if (outer != null && outer.nested!= null && outer.nested.inner != null) {
System.out.println(outer.nested.inner.foo);
}
让我们干掉所有的空检查,使用java8Optional类型。map方法输入参数是lambda表达式,自动封装每个函数返回值至Optional,且可以管道式连接多个map操作,null检查自动完成。
Optional.of(new Outer())
.map(Outer::getNested)
.map(Nested::getInner)
.map(Inner::getFoo)
.ifPresent(System.out::println);
3. 认识下Optional类
Optional类是一个包装非空对象的容器类型。Optional对象使用缺失值表示null值,其表达的意思即为值有可能为null。意在在java系统中减少空指针异常。看下面示例:
public Optional<Customer>findCustomerWithSSN(String ssn) {
...
}
Optional<Customer> optional = findCustomerWithSSN(ssn);
if (optional.isPresent()) {
Customercustomer = maybeCustomer.get();
... usecustomer ...
}
else {
...deal with absence case ...
}
当然最好还是使用ifPresent。
optional.ifPresent(ssn -> {
String s = ssn.toUpperCase();
System.out.println("The longest name is "+ s);
});
findCustomerWithSSN(ssn).ifPresent(() ->
System.out.println("customerexists!"));
也可以使用orElse。
Long value =findOptionalLong(ssn).orElse(0L);
4. Optional类使用场景
Optional类应该作为可能有返回值函数的返回值类型。有人甚至建议Optional类应该改名为OptionalReturn。
Optional类不是为了避免所有的空指针类型机制。方法或构造函数输入参数强制性检查就仍然是有必要的。
在以下场景一般不建议使用Optional类。
- 领域模型层(非序列化)
- 数据传输对象(同上原因)
- 方法的输入参数
- 构造函数参数
5. Optional类方法参考
下面摘抄Optional类的方法,供参考。
序号 | 方法和描述 |
1 | static <T> Optional<T> empty() Returns an empty Optional instance. |
2 | boolean equals(Object obj) Indicates whether some other object is “equal to” this Optional. |
3 | Optional<T> filter(Predicate<? super <T> predicate) If a value is present and the value matches a given predicate, it returns an Optional describing the value, otherwise returns an empty Optional. |
4 | <U> Optional<U> flatMap(Function<? super T,Optional<U>> mapper) If a value is present, it applies the provided Optional-bearing mapping function to it, returns that result, otherwise returns an empty Optional. |
5 | T get() If a value is present in this Optional, returns the value, otherwise throws NoSuchElementException. |
6 | int hashCode() Returns the hash code value of the present value, if any, or 0 (zero) if no value is present. |
7 | void ifPresent(Consumer<? super T> consumer) If a value is present, it invokes the specified consumer with the value, otherwise does nothing. |
8 | boolean isPresent() Returns true if there is a value present, otherwise false. |
9 | <U>Optional<U> map(Function<? super T,? extends U> mapper) If a value is present, applies the provided mapping function to it, and if the result is non-null, returns an Optional describing the result. |
10 | static <T> Optional<T> of(T value) Returns an Optional with the specified present non-null value. |
11 | static <T> Optional<T> ofNullable(T value) Returns an Optional describing the specified value, if non-null, otherwise returns an empty Optional. |
12 | T orElse(T other) Returns the value if present, otherwise returns other. |
13 | T orElseGet(Supplier<? extends T> other) Returns the value if present, otherwise invokes other and returns the result of that invocation. |
14 | <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) Returns the contained value, if present, otherwise throws an exception to be created by the provided supplier. |
15 | String toString() Returns a non-empty string representation of this Optional suitable for debugging. |