咱们知道 Java 8 增长了一些颇有用的 API, 其中一个就是 Optional. 若是对它不稍假探索, 只是轻描淡写的认为它能够优雅的解决 NullPointException 的问题, 因而代码就开始这么写了java
Optional<User> user = ...... if (user.isPresent()) { return user.getOrders(); } else { return Collections.emptyList(); }
那么不得不说咱们的思惟仍然是在原地踏步, 只是本能的认为它不过是 User 实例的包装, 这与咱们以前写成安全
User user = ..... if (user != null) { return user.getOrders(); } else { return Collections.emptyList(); }
实质上是没有任何分别. 这就是咱们将要讲到的使用好 Java 8 Optional 类型的正确姿式.app
在里约奥运之时, 新闻一再提起五星红旗有问题, 但是我怎么看都看不出来有什么问题, 后来才道是小星星膜拜中央的姿式不对. 所以咱们千万也别对本身习觉得常的事情以为理所固然, 丝绝不会以为有何不妥, 换句话说也就是当咱们切换到 Java 8 的 Optional 时, 不能继承性的对待过往 null 时的那种思惟, 应该掌握好新的, 正确的使用 Java 8 Optional 的正确姿式.ide
直白的讲, 当咱们还在以以下几种方式使用 Optional 时, 就得开始检视本身了函数
isPresent() 与 obj != null 无任何分别, 咱们的生活依然在步步惊心. 而没有 isPresent() 做铺垫的 get() 调用在 IntelliJ IDEA 中会收到告警fetch
Reports calls to java.util.Optional.get() without first checking with a isPresent() call if a value is available. If the Optional does not contain a value, get() will throw an exception. (调用 Optional.get() 前不事先用 isPresent() 检查值是否可用. 假如 Optional 不包含一个值, get() 将会抛出一个异常)google
把 Optional 类型用做属性或是方法参数在 IntelliJ IDEA 中更是强力不推荐的spa
Reports any uses of java.util.Optional<T>, java.util.OptionalDouble, java.util.OptionalInt, java.util.OptionalLong or com.google.common.base.Optional as the type for a field or a parameter. Optional was designed to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result". Using a field with type java.util.Optional is also problematic if the class needs to be Serializable, which java.util.Optional is not. (使用任何像 Optional 的类型做为字段或方法参数都是不可取的. Optional 只设计为类库方法的, 可明确表示可能无值状况下的返回类型. Optional 类型不可被序列化, 用做字段类型会出问题的)设计
因此 Optional 中咱们真正可依赖的应该是除了 isPresent() 和 get() 的其余方法:指针
我略有自信的按照它们大概使用频度对上面的方法排了一下序.
先又不得不提一下 Optional 的三种构造方式: Optional.of(obj) , Optional.ofNullable(obj) 和明确的 Optional.empty()
Optional.of(obj) : 它要求传入的 obj 不能是 null 值的, 不然还没开始进入角色就倒在了 NullPointerException 异常上了.
Optional.ofNullable(obj) : 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就获得 Optional.empty() , 非 null 就调用 Optional.of(obj) .
那是否是咱们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就好了呢? 那也未必, 不然 Optional.of(obj) 何须如此暴露呢, 私有则可?
我本人的观点是: 1. 当咱们很是很是的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 好比它是一个刚 new 出来的对象( Optional.of(new User(...)) ), 或者是一个非 null 常量时; 2. 当想为 obj 断言不为 null 时, 即咱们想在万一 obj 为 null 当即报告 NullPointException 异常, 当即修改, 而不是隐藏空指针异常时, 咱们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.
如今才开始怎么去使用一个已有的 Optional 实例, 假定咱们有一个实例 Optional<User> user , 下面是几个广泛的, 应避免 if(user.isPresent()) { ... } else { ... } 几中应用方式.
return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null; return user.orElse(UNKNOWN_USER);
return user.orElseGet(() -> fetchAUserFromDatabase()); //而不要 return user.isPresent() ? user: fetchAUserFromDatabase();
user.ifPresent(System.out::println); //而不要下边那样 if (user.isPresent()) { System.out.println(user.get()); }
当 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(); if(name != null) { return name.toUpperCase(); } else { return null; } } else { return null; }
针对这方面 Groovy 提供了一种安全的属性/方法访问操做符 ?.
user?.getUsername()?.toUpperCase();
Swift 也有相似的语法, 只做用在 Optional 的类型上.
用了 isPresent() 处理 NullPointerException 不叫优雅, 有了 orElse, orElseGet 等, 特别是 map 方法才叫优雅.
其余几个, filter() 把不符合条件的值变为 empty() , flatMap() 老是与 map() 方法成对的, orElseThrow() 在有值时直接返回, 无值时抛出想要的异常.
一句话小结: 使用 Optional 时尽可能不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其余像 Optional.orElse() , Optional.orElseGet() , Optional.map() 等这样的方法.
最后, 最好的理解 Java 8 Optional 的方法莫过于看它的源代码java.util.Optional , 阅读了源代码才能真真正正的让你解释起来最有底气, Optional 的方法中基本都是内部调用 isPresent() 判断, 真时处理值, 假时什么也不作.