咱們知道 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() 判斷, 真時處理值, 假時什麼也不作.