Optional原始碼解析與實踐
1 導讀
NullPointerException在開發過程中經常遇到,稍有不慎小BUG就出現了,如果避免這個問題呢,Optional就是專門解決這個問題的類,那麼Optional如何使用呢?讓我們一起探索一下吧!
2 原始碼解析
2.1 Optional定義
Optional類是Java8為了解決null值判斷問題而建立的容器類,在java.util 下,使用Optional類可以避免顯式的null值判斷,避免null導致的NullPointerException。首先,Optional是一個容器,它可以儲存型別T的值,也可以為null的容器物件。Optional容器只能存一個值。
2.2 Optional的屬性
1)原始碼:
/** * Common instance for {@code private static final Optional<?> EMPTY = new Optional<>(); /** * If non-null, the value; if null, indicates no value is present */ private final T value;
根據原始碼可以看到Optional有兩個屬性,一個是為空值準備的EMPTY和泛型值value;
2.3 Optional的方法
Optional除toString()、hashCode() 、equals()等Object的方法外,還包含以下方法。
2.3.1 私有構造方法
/** * Constructs an empty instance. * * @implNote Generally only one empty instance, {@link Optional#EMPTY}, * should exist per VM. */ private Optional() { this.value = null; } /** * Constructs an instance with the value present. * * @param value the non-null value to be present * @throws NullPointerException if value is null */ private Optional(T value) { this.value = Objects.requireNonNull(value); }
分別是建立一個空例項和構造一個具有當前值的例項。
2.3.2 建立方法
1)原始碼
public static<T> Optional<T> empty() { @SuppressWarnings("unchecked") Optional<T> t = (Optional<T>) EMPTY; return t; } public static <T> Optional<T> of(T value) { return new Optional<>(value); } public static <T> Optional<T> ofNullable(T value) { return value == null ? empty() : of(value); }
2)方法說明
- empty(): 建立一個空的 Optional 例項
- of(T t) : 建立一個 Optional 例項,當 t為null時丟擲異常
- ofNullable(T t): 建立一個 Optional 例項,但當 t為null時不會丟擲異常,而是返回一個空的例項
3)測試程式碼
public static void main(String[] args) { Integer value1 = null; Integer value2 = 1; try { Optional<Integer> optional1 = Optional.empty(); System.out.println("optional1建立了"); }catch (Exception e){ System.out.println("optional1失敗了"); } try { Optional<Integer> optional2 = Optional.of(value1); System.out.println("optional2建立了"); }catch (Exception e){ System.out.println("optional2失敗了"); } try { Optional<Integer> optional3 = Optional.ofNullable(value1); System.out.println("optional3建立了"); }catch (Exception e){ System.out.println("optional3失敗了"); } try { Optional<Integer> optional4 = Optional.of(value2); System.out.println("optional4建立了"); }catch (Exception e){ System.out.println("optional4失敗了"); } try { Optional<Integer> optional5 = Optional.ofNullable(value2); System.out.println("optional5建立了"); }catch (Exception e){ System.out.println("optional5失敗了"); } }
4)執行結果
2.3.3 值獲取方法
1)原始碼
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
2)方法說明
get(): 如果Optional不為空,則返回該Optional容器中的值,否則丟擲NoSuchElementExceptio 。
3)測試程式碼
public static void main(String[] args) { Integer value1 = null; Integer value2 = 1; Optional<Integer> optional1 = Optional.ofNullable(value1); Optional<Integer> optional2 = Optional.of(value2); try { Integer result=optional1.get(); System.out.println("optional1的值是:"+result); }catch (Exception e){ System.out.println("optional1的值獲取失敗,原因:"+e.getMessage()); } try { Integer result=optional2.get(); System.out.println("optional2的值是:"+result); }catch (Exception e){ System.out.println("optional2的值獲取失敗,原因:"+e.getMessage()); } }
4)執行結果
2.3.4 判斷方法
1)原始碼
public boolean isPresent() { return value != null; } public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); } public T orElse(T other) { return value != null ? value : other; } public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); } public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X { if (value != null) { return value; } else { throw exceptionSupplier.get(); } }
2)方法說明
- isPresent(): 判斷optional是否為空,如果空則返回false,否則返回true
- ifPresent(Consumer c): 如果optional不為空,則將optional中的物件傳給Comsumer函式
- orElse(T other): 如果optional不為空,則返回optional中的物件;如果為null,則返回 other 這個物件。
- orElseGet(Supplier other): 如果optional不為空,則返回optional中的物件;如果為null,否則呼叫其他函式並返回撥用的結果
- orElseThrow(Supplier exception): 如果optional不為空,則返回optional中的物件;如果為null,則丟擲Supplier函式生成的異常
3)測試程式碼
public static void main(String[] args) { Integer value1 = null; Integer value2 = 1; Optional<Integer> optional1 = Optional.ofNullable(value1); Optional<Integer> optional2 = Optional.of(value2); try { if(optional1.isPresent()){ System.out.println("optional1的isPresent結果不為空"); }else{ System.out.println("optional1的isPresent結果為空"); } }catch (Exception e){ System.out.println("optional1的isPresent判空失敗,原因:"+e.getMessage()); } try { if(optional2.isPresent()){ System.out.println("optional2的isPresent結果不為空"); }else{ System.out.println("optional2的isPresent結果為空"); } }catch (Exception e){ System.out.println("optional2的isPresent判空失敗,原因:"+e.getMessage()); } optional1.ifPresent(t->{ int i =t+1; System.out.println("optional1處理後的值是"+i); }); optional2.ifPresent(t->{ int i =t+1; System.out.println("optional2處理後的值是"+i);}); Integer value3 = 2; Integer result = optional1.orElse(value3); System.out.println("optional1執行orElse處理後的值是"+result); result = optional2.orElse(value3); System.out.println("optional2執行orElse處理後的值是"+result); result = optional1.orElseGet(()-> new Integer(-1)); System.out.println("optional1執行orElseGet處理後的值是"+result); result = optional2.orElseGet(()-> new Integer(-1)); System.out.println("optional2執行orElseGet處理後的值是"+result); try { result = optional1.orElseThrow (()-> new RuntimeException("值是空的")); System.out.println("optional1執行orElseThrow處理後的值是"+result); }catch (Exception e){ System.out.println("optional1的orElseThrow丟擲異常:"+e.getMessage()); } try { result = optional2.orElseThrow (()-> new RuntimeException("值是空的")); System.out.println("optional2執行orElseThrow處理後的值是"+result); }catch (Exception e){ System.out.println("optional2的orElseThrow丟擲異常:"+e.getMessage());
4)執行結果
2.3.5 過濾方法
1)原始碼
public Optional<T> filter(Predicate<? super T> predicate) { Objects.requireNonNull(predicate); if (!isPresent()) return this; else return predicate.test(value) ? this : empty(); }
2)方法說明
filter(Predicate p): 如果optional不為空,則執行Predicate p,如果p的結果為true,則返回原本的optional,否則返回空的optional
3)測試程式碼
public static void main(String[] args) { Integer value1 = 5; Integer value2 = 6; Optional<Integer> optional1 = Optional.ofNullable(value1); Optional<Integer> optional2 = Optional.of(value2); Optional<Integer> result =optional1.filter(t->t > 5); System.out.println("optional1的filter後的值:"+result); result =optional2.filter(t->t > 5); System.out.println("optional2的filter後的值:"+result);
4)執行結果
2.3.6 對映方法
1)原始碼
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Optional.ofNullable(mapper.apply(value)); } } public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) { Objects.requireNonNull(mapper); if (!isPresent()) return empty(); else { return Objects.requireNonNull(mapper.apply(value)); } }
2)方法說明
- map(Function mapper): 如果存在一個值,則對其應用提供的對映函式,如果結果非空,則返回描述結果的Optional。 否則返回一個空的Optional。
- flatMap(Function< T,Optional> mapper): 如果有值,則對其應用提供的可選對映函式,返回結果,否則返回空的可選函式。 這個方法類似於map(Function),但是提供的對映器的結果已經是一個可選的,如果呼叫,flatMap不會用額外的可選的包裝它。
- 區別:map會自動將u放到optional中,而flatMap則需要手動給u建立一個optional
3)測試程式碼
public static void main(String[] args) { User user1 = null; User user2 = new User("user2名字",19); Optional<User> optional1 = Optional.ofNullable(user1); Optional<User> optional2 = Optional.of(user2); System.out.println("=========map=========="); System.out.println("optional1的map前的值:"+optional1); Optional<String> result =optional1.map(t->t.getName()); System.out.println("optional1的map後的值:"+result); System.out.println("optional2的map前的值:"+optional2); result =optional2.map(t->t.getName()); System.out.println("optional2的map後的值:"+result); System.out.println("===========flatMap========"); System.out.println("optional1的flatMap前的值:"+optional1); Optional<Integer> result2 =optional1.flatMap(t->Optional.ofNullable(t.getAge())); System.out.println("optional1的flatMap後的值:"+result2); System.out.println("optional2的flatMap前的值:"+optional2); result2 =optional2.flatMap(t->Optional.ofNullable(t.getAge())); System.out.println("optional2的flatMap後的值:"+result2); } public class User { String name; Integer age; public User(String name,Integer age){ this.name = name; this.age=age; } public String getName() { return name; } public Integer getAge() { return age;
4)執行結果
3 應用例項
3.1 錯誤用法
- 由於Optional並沒有實現Serializable介面,所以不能作為類的屬性。
- 不要把Optional作為方法的引數。
- 把if(x!=null)直接換成Optional.ofNullable(x).isPresent(),這樣有過度編碼的嫌疑。
- 直接使用Optional.get()的返回值進行操作,String result =Optional.ofNullable(null).get().toString();這樣還是會丟擲異常的。
3.2 建議用法
A類有屬性B類,B類有屬性C類,C類有name這個欄位。
使用Optional之前:
if(atest!=null){ Btest btest =atest.getBtest(); if(btest!=null){ Ctest ctest = btest.getCtest(); if (ctest != null) { name =ctest.getName(); } } }
使用Optional之後:
name = Optional.ofNullable(atest).map(t->t.getBtest()).map(t->t.getCtest()).map(t->t.getName()).orElse("預設值");
程式碼是不是看上去更整潔了呢?
4 總結
透過對Optional原始碼解析和用例測試程式碼的執行結果,可以看出使用Optional可以最佳化null值判斷程式碼,讓程式碼變得更加優雅和整潔。
作者:陳昌浩
相關文章
- Function原始碼解析與實踐2022-11-29Function原始碼
- QT Widgets模組原始碼解析與實踐2024-09-22QT原始碼
- Optional的使用與解析2024-12-05
- Redis核心原理與實踐--事務實踐與原始碼分析2021-11-10Redis原始碼
- redux 原始碼解析與實際應用2018-11-15Redux原始碼
- Java Optional使用的最佳實踐2019-04-05Java
- ItemDecoration深入解析與實戰(一)——原始碼分析2019-02-22原始碼
- EventBus原理與原始碼解析2018-10-25原始碼
- Netty原始碼解析 -- ChannelOutboundBuffer實現與Flush過程2020-11-08Netty原始碼
- Dubbo 實現原理與原始碼解析系列 —— 精品合集2018-06-02原始碼
- vscode外掛開發實踐與demo原始碼2019-03-02VSCode原始碼
- 使用Java Optional類的最佳實踐 - oracle2021-03-06JavaOracle
- PyTorch ResNet 使用與原始碼解析2020-09-08PyTorch原始碼
- Netty原始碼解析 -- FastThreadLocal與HashedWheelTimer2021-01-17Netty原始碼ASTthread
- co原始碼分析及其實踐2019-02-16原始碼
- rocketmq-spring : 實戰與原始碼解析一網打盡2023-04-02MQSpring原始碼
- 比特幣挖礦與原始碼解析2021-12-20比特幣原始碼
- Istio 技術與實踐01: 原始碼解析之 Pilot 多雲平臺服務發現機制2019-03-01原始碼
- 深入原始碼解析 tapable 實現原理2019-11-05原始碼
- Netty原始碼解析 -- PoolSubpage實現原理2020-12-19Netty原始碼
- Netty原始碼解析 -- PoolChunk實現原理2020-12-06Netty原始碼
- spring原始碼解析 (七) 事務底層原始碼實現2020-12-03Spring原始碼
- Redis核心原理與實踐--Redis啟動過程原始碼分析2021-10-28Redis原始碼
- 最佳實踐 | 原始碼升級gcc2019-01-17原始碼GC
- QT Widgets模組原始碼解析與技巧2024-09-22QT原始碼
- Tomcat長輪詢原理與原始碼解析2023-04-16Tomcat原始碼
- 【原始碼解析】AsyncTask的用法與規則2019-11-14原始碼
- OkHttp 開源庫使用與原始碼解析2019-03-09HTTP原始碼
- 【原始碼解析】- ArrayList原始碼解析,絕對詳細2021-04-15原始碼
- Java Timer原始碼解析(定時器原始碼解析)2018-10-20Java原始碼定時器
- 運維DevOps體系解析與落地實踐2022-12-07運維dev
- 深入解析Rivest Cipher 4:理論與實踐2024-04-17
- Docker容器編排技術解析與實踐2024-04-01Docker
- WebSocket原理與實踐(三)--解析資料幀2018-03-11Web
- 原始碼解析.Net中DependencyInjection的實現2021-08-31原始碼
- 原始碼解析.Net中Middleware的實現2021-09-03原始碼
- Node.js原始碼解析-Readable實現2019-02-16Node.js原始碼
- InnoDB MVCC實現原理及原始碼解析2018-04-15MVC原始碼