首頁 > Java > java教程 > 主體

Java 8新增的API--Optional 的使用實例

零下一度
發布: 2017-05-24 11:40:22
原創
2009 人瀏覽過

我們知道Java 8 增加了一些很有用的API, 其中一個就是Optional. 如果對它不稍假探索, 只是輕描淡寫的認為它可以優雅的解決NullPointException 的問題, 於是程式碼就開始這麼寫了

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 類型的正確姿勢.

在裡約奧運之時, 新聞一再提起五星級紅旗有問題, 可是我怎麼看都看不出來有什麼問題, 後來才道是小星星膜拜中央的姿勢不對. 因此我們千萬也別對自己習以為常的事情覺得理所當然, 絲毫不會覺得有何不妥, 換句話說當我們切換到Java 8 的Optional 時, 不能繼承性的對待過往null 時的那種思維, 應該掌握好新的, 正確的使用Java 8 Optional 的正確姿勢.

直白的講, 當我們還在以如下幾種方式使用Optional 時,就得開始檢視自己了

  1. 呼叫isPresent()  方法時

  2. 呼叫get()  方法時

  3. #Optional 類型作為類別/實例屬性時

  4. Optional 類型作為方法參數時

isPresent() 與obj != null無任何分別, 我們的生活依然在步步驚心. 而沒有isPresent() 作鋪墊的get() 調用在IntelliJ IDEA 中會收到告警

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() 將會拋出一個異常)

把Optional 類型用作屬性或是方法參數在IntelliJ IDEA 中更是強力不推薦的

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() 的其他方法:

public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
public T orElse(T other)
public T orElseGet(Supplier<? extends T> other)
public void ifPresent(Consumer<? super T> consumer)
public Optional<T> filter(Predicate<? super T> predicate)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X
登入後複製

        我略有自信的按照它們大概使用頻度對上面的方法排了一下序.

先我不得不提一下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 , 下面是几个普遍的, 应避免 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());
}
登入後複製

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();  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 的方法莫過於看它的源代碼.util.Optionaljava.util.Optional , 閱讀了原始碼才能真真正正的讓你解釋起來最有底氣, Optional 的方法中基本上都是內部調用  isPresent() 判斷, 真時處理值, 假時什麼也不做.

【相關推薦】

1. 分享Java8中新引入的類別Optional實例代碼

2. 解析Java 8 Optional類別實例教程

以上是Java 8新增的API--Optional 的使用實例的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板