# 如何正确使用 Optional
Java 8 增加了一些很有用的 API,其中一个就是 Optional。目的是从 JDK 级别解决 NPE 问题。大多数新语言都从语法层面上解决了 NPE 问题。
你『不应该』以如下方式使用 Optional:
调用它的 isPresent 方法
调用它的 get 方法
用 Optional 类型作为类/实例属性
用 Optional 类型作为方法参数
一句话小结: 使用 Optional 时尽量不要直接调用 Optional 的 get 方法, Optional 的 isPresent 方法更应该被视为一个私有方法。
# 基本使用
Optional 的 3 种构造方式:
明确的 Optional.empty()
它是用来构造一个 Optional 空容器。实际上使用它的机会几乎没有。
Optional.of(obj)
它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了。
Optional.ofNullable(obj)
它以更智能、宽容的方式来构造一个 Optional 实例,来者不拒。传 null 进到就等价于调用
Optional.empty()
, 非 null 就等价于调用Optional.of(obj)
。
使用 Optional.of(obj) 来构造 Optional 实例的场景:
当我们非常的明确将要传给
Optional.of(obj)
的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(例如,Optional.of(new User(...))
), 或者是一个非 null 常量时;逻辑上 obj 明确不允许为 null 。一旦为 null ,立即报错,抛出 NPE 。不允许接续执行。
那怎么去使用一个已有的 Optional 实例?假定我们有一个实例 Optional<User> user。应避免 if(user.isPresent()) { ... } else { ... } 的方式进行应用。
存在即返回, 无则提供默认值:
return user.orElse(null);
return user.orElse(UNKNOWN_USER);
存在即返回, 无则由函数来产生:
return user.orElseGet(() -> fetchAUserFromDatabase());
存在才对它做点什么:
user.ifPresent(System.out::println);
/* 而不要下边那样
if (user.isPresent()) {
System.out.println(user.get());
}
*/
# map 函数隆重登场
当 user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse(), orElseGet() 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行:
return user.map(u -> u.getOrders()).orElse(Collections.emptyList())
/* 上面避免了我们类似 Java 8 之前的做法
if(user.isPresent()) {
return user.get().getOrders();
} else {
return Collections.emptyList();
}
*/
map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式
return user.map(u -> u.getUsername())
.map(name -> name.toUpperCase())
.orElse(null);
这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断
User user = .....
if (user != null) {
String name = user.getUsername();
return name != null ? name.toUpperCase() : null;
} else {
return null;
}
# Optional 的使用场景和使用原则
Optional 通常(只应该)出现在方法的返回值类型部分。
public Optional<Department> selectDepartmentByPrimaryKey(Integer deptno) {
...
}
Optional<Department>
和 Department
两种不同类型的返回值,代表着方法的作者对方法的使用者的两种『暗示』和『约定』:
Department
类型的返回值由方法的作者确保该方法 一定会/必须要 返回一个非空的 Department 对象。方法的使用者无须判断/担心该方法的返回值为 null 的问题。
即便是出现了返回 null 的问题,责任在方法的作者一方,由他负责修改/调整代码,务必确保该方法返回值为非空。
Optional<Department>
类型的返回值方法的作者在暗示使用者,本方法的返回值 Optional 容器中存放的 Department 对象可能存在,也可能不存在(为 Null)。逻辑上,相当于本方法的返回值有两种可能。
由方法的使用者,在从 Optional 中取出 Department 对象时来判断-处理有可能为 null 的情况。
此时,方法的使用者,必须通过
.orElseGet()
或.orElseThrow()
方法从 Optional 中取值(一定不要使用get()
方法从中取 Department 。
简而言之,Optional<T>
类型的返回值,就是在强迫你思考:这个方法的返回值有可能是 NULL,接下来你需要对这种情况作出处理(给它个默认值,或者抛出异常) 。