JDK8 Optional 應用場景與方式分析

原創文章, 轉載請私信. 訂閱號 tastejava 學習加思考, 仔細品味java之美

Optional 出現的意義

Optional 是從 JDK1.8 開始提供的一個容器類, 主要用於避免空指針異常(NPE), 其提供的一系列方法配合 Lambda 表達式可以讓代碼更加清晰, 語義化, 以及避免了空指針異常的問題這裏要注意是避免空指針異常, 而不是避免返回null.

Optional 源碼分析及使用

關於 Optional 方法怎麼使用的資料網絡上也有很多了, 不過一些文章不是很清晰明確, 講述 Api 之外並沒有講實際應用或者應用的意義在哪. 今天還是一樣從源代碼來分析 Optional 怎麼使用, 以及其作用.

什麼是 Optional

源碼中註釋已經說得比較清晰了, 我來摘錄幾句

Optional 是一個容器對象, 可能包含一個非空值也可能不包含. 包含值時isPresent方法會返回true, get 方法會返回包含的值.
Optional 是一個 value-based 類, 也就是說 Optional 包含的值相等時, 實例的hashCode方法返回值相等, equals 方法返回 true , 需要用到比較兩個 Optional 實例是否相等的地方要注意, 如 == equals hashCode 或者 synchronization

Optional 方法的使用

JDK1.8 版本的 Optional 總共有17個方法, 除去兩個私有的構造方法, 除去equals, hashCode, toString三個方法外, 我們來分析一下剩下12個 Optional 提供的方法.
先來看三個構造 Optional 實例的方法

  1. empty 方法返回一個不包含值的 Optional 實例, 注意不保證返回的 empty 是單例, 不要用 == 比較.
    public static<T> Optional<T> empty();
  1. 返回一個 Optional 實例, 代表指定的非空值, 如果傳入 null 會立刻拋出空指針異常
	public static <T> Optional<T> of(T value);
  1. 返回一個 Optional 實例, 如果指定非空值則實例包含非空值, 如果傳入 null 返回不包含值的 empty
	public static <T> Optional<T> ofNullable(T value);

下面來看 Optional 中兩個讓人唾棄的方法, 不推薦使用

  1. isPresent 用來判斷實例是否包含值, 如果不包含非空值返回 false, 否則返回 true
	public boolean isPresent();
  1. get 方法, 如果實例包含值則返回當前值, 否則拋出 NoSushElementException 異常.
	public T get();

爲什麼不推薦調用上面兩個方法呢, 因爲容易寫出下方代碼, 比原先判斷 if null 的代碼還髒.

		Integer result;
        if (optional.isPresent()) {
            result = optional.get();
        }

接下里我們來看 Optional 中真正有用的7個方法

  1. ifPresent 方法作用是當實例包含值時, 來執行傳入的 Consumer, 比如調用一些其他方法.
	public void ifPresent(Consumer<? super T> consumer);
  1. filter 方法用於過濾不符合條件的值, 接收一個 Predicate 參數, 如果符合條件返回代表值的 Optional 實例, 否則返回 empty
	public Optional<T> filter(Predicate<? super T> predicate)
  1. map 方法是鏈式調用避免空指針的核心方法, 當實例包含值時, 對值執行傳入的 Function 邏輯, 並返回一個代表結果值的新的 Optional 實例. 這也意味着返回的結果依舊可以繼續調用 map 方法, 而不需要空指針判斷.方法簽名以及示例如下:
	public<U> Optional<U> map(Function<? super T, ? extends U> mapper);
	// 如果 outer 包含的 nested 對象或者 nested 包含的 inner 對象爲空
	// 傳統方式 每一個 get 方法都要進行空判斷, 否則有可能拋出空指針異常
	// 用 Optional map 實現鏈式調用, 而不需要判斷 null
	// 任何一層對象爲 null 時, 變量獲得的都是指定默認值 null
	String name = Optional.of(outter).map(Outter::getNested)
	                .map(Nested::getInner)
	                .map(Inner::getName).orElse(null);
  1. flatMap 方法與 map 方法作用一致, 不過 flatMap 接收的參數 Function 要求返回一個 Optional 實例, 並且 flatMap 方法直接返回該結果, 而不對結果包裝一層 Optional, 適用於 Optional 包含的值也是 Optional, 可以進行多層 Optional 的合併.
	public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper);

** 接下來看三個也比較重要的方法, orElse, orElseGet, orElseThrow**
10. 如果實例包含值, 那麼返回這個值, 否則返回指定的默認值, 如null

	public T orElse(T other);
  1. 如果實例包含值, 返回這個值, 否則調用傳入的 Supplier 參數生產一個值.
	public T orElseGet(Supplier<? extends T> other);
  1. 如果實例不包含值, 調用傳入的 Supplier 參數, 生成一個異常實例並拋出.這個方法通常與全局異常處理器一起使用, 當參數或者其他情況獲取不到值時, 拋出自定義異常, 由異常處理器處理成通用返回結果, 返回給前端.
	public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier);

實際開發中 Optional 的應用

在使用 Repository 操作 Es 時, findById 通過 id 查詢數據庫記錄, 返回結果爲 Optional, 此時無需 if null 判斷, 利用 orElseThrow , 不存在就拋出異常, 配合異常處理器渲染通用結果和錯誤提示信息返回給前端處理.

Optional<ApiManager> byId = apiManagerRepository
		.findById(apiManagerDTO.getId());
ApiManager oldApiManager = byId
		.orElseThrow(
		() -> new SysException(SysCodeEnum.API_MANAGER_DOES_NOT_EXIST)
		);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章