Java Optional使用的最佳實踐
這是piotr szybicki4年來為了解正確使用Optional型別而努力的結果。
Optional隱藏了可能存在空指標的不確定性,比如:
List<String> numbers= ImmutableList.of("ONE", "TWO", "THREE"); return numbers.stream() .filter(number -> "FOUR".equals(number)) .findAny() .toLoweCase(); |
結果會導致NullPointerExceptions空指標錯誤,而使用if else進行判斷是一種切割器cutter味道:
List<String> numbers= ImmutableList.of("ONE", "TWO", "THREE"); String numberThatImLookingFour = numbers.stream() .filter(number -> "FOUR".equals(number)) .findAny(); if(numberThatImLookingFour != null){ return numberThatImLookingFour.toLowerCase(); }else{ return "not found"; } |
所以Brian Goetz和Steward Marks(Java語言架構師)聚在一起寫下了以下段落:
我們的目的是為庫方法的返回型別提供一種有限的機制,其中需要一種明確的方式來表示“無結果”,並且對於這樣的方法使用null絕對可能導致錯誤。
因此新增了Optional <T>,這不是一個真正的新概念,歷史可以追溯到Haskell的Maybe monad。突然間,我們獲得了JDK批准的表示可能存在或不存在的值的方式,和往常一樣,在各地使用新功能的開發人員抓狂了。
空引用空指標是無數個錯誤的來源,通常也不能很好地標識“不存在”這個概念。
應該如何處理這個問題的重要部分來自DDD。在有界上下文的入口處我們說:' 你不能透過 ':
傳入領域的所有變數是執行業務邏輯所需的,我們必須減少編寫if(obj == null)檢查判斷的程式碼行數量。我們仍然需要與外部世界(資料庫查詢,REST端點等)進行互動,並根據執行的邏輯互動輸出。如果使用Optional 得當則可以提供幫助。
什麼是Optional?
- 它是box型別,保持對另一個物件的引用。
- 是不可變的,不可序列化的
- 沒有公共建構函式
- 只能是present 或absent
- 透過of(), ofNullable(), empty() 靜態方法建立。
從這個盒子box中如何獲取值?
- get()
- orElse()
- orElseGet()
- orElseThrow()
有一種誘惑是呼叫get()來獲取其中的值。我們都知道普通JavaBean的getter / setters :)。並且期望如果我們呼叫get ...()我們就會得到一些東西。當呼叫普通bean的getter時,你永遠不會得到任何丟擲的異常。但是,如果呼叫在optional上呼叫get方法,並且該選項內部為空時,則會丟擲異常NoSuchElementException。
這些方法應該被稱為getOrThorwSomeHorribleError(),因此第一和第二條規則:
#1不要將null賦給Optional
#2避免使用Optional.get()。如果你不能證明存在可選項,那麼永遠不要呼叫get()。
使用orElse(), orElseGet(), orElseThrow().獲得你的結果。
可以重構以下程式碼:
String variable = fetchSomeVaraible(); if(variable == null){ 1. throw new IllegalStateException("No such variable"); 2. return createVariable(); 3. return "new variable"; } else { ... 100 lines of code ... } |
重構到:
1. Optional<String> variableOpt = fetchOptionalSomeVaraible(); String variable = variableOpt.orElseThrow(() -> new Exeption("")) ... 100 lines of code ... 2. Optional<String> variableOpt = fetchOptionalSomeVaraible(); String variable = variableOpt.orElseGet(() -> createVariable()) ... 100 lines of code ... 3. Optional<String> variableOpt = fetchOptionalSomeVaraible(); String variable = variableOpt.orElse("new variable") ... 100 lines of code ... |
注意,orElse(..)是急切計算,意味著下面程式碼:
Optional<Dog> optionalDog = fetchOptionalDog(); optionalDog .map(this::printUserAndReturnUser) .orElse(this::printVoidAndReturnUser) |
如果值存在則將執行兩個方法,如果值不存在,則僅執行最後一個方法。為了處理這些情況,我們可以使用方法orElseGet(),它將supplier 作為引數,並且是惰性計算的。
#3不要在欄位,方法引數,集合中使用Optional。
下面是將thatField直接賦值給了類欄位:
public void setThatField(Optional <ThatFieldType> thatField){ this.thatField = thatField; } |
.
改為:
setThatField(Optional.ofNullable(thatField)); |
#4只有每當結果不確定時,使用Optional作為返回型別。。
說實話,這是使用Optional的唯一好地方。我將複製貼上前面的話:
我們的目的是為庫方法的返回型別提供一種有限的機制,其中需要一種明確的方式來表示“無結果”,並且對於這樣的方法使用null 絕對可能導致錯誤。
#5不要害怕使用map和filter。
有一些值得遵循的一般開發實踐稱為SLA-p:Single Layer of Abstraction字母的第一個大寫。
下面是需要被重構程式碼:
Dog dog = fetchSomeVaraible(); String dogString = dogToString(dog); public String dogToString(Dog dog){ if(dog == null){ return "DOG'd name is : " + dog.getName(); } else { return "CAT"; } } |
重構到:
Optional<Dog> dog = fetchDogIfExists(); String dogsName = dog .map(this::convertToDog) .orElseGet(this::convertToCat) public void convertToDog(Dog dog){ return "DOG'd name is : " + dog.getName(); } public void convertToCat(){ return "CAT"; } |
Filter是有用的摺疊語法:
Dog dog = fetchDog(); if(optionalDog != null && optionalDog.isBigDog()){ doBlaBlaBla(optionalDog); } |
上面程式碼可以被重構為:
Optional<Dog> optionalDog = fetchOptionalDog(); optionalDog .filter(Dog::isBigDog) .ifPresent(this::doBlaBlaBla) |
#6不要為了鏈方法而使用optional 。
使用optional 時要注意的一件事是鏈式方法的誘惑。當我們像構建器模式一樣連結方法時,事情可能看起來很漂亮:)。但並不總是等於更具可讀性。所以不要這樣做:
Optional .ofNullable(someVariable) .ifPresent(this::blablabla) |
它對效能不利,對可讀性也不好。我們應儘可能避免使用null引用。
#7使所有表示式成為單行lambda
這是更普遍的規則,我認為也應該應用於流。但這篇文章是關於optional 。使用Optional 重要點是記住等式左邊和右邊一樣重要:
Optional .ofNullable(someVariable) .map(variable -> { try{ return someREpozitory.findById(variable.getIdOfOtherObject()); } catch (IOException e){ LOGGER.error(e); throw new RuntimeException(e); }}) .filter(variable -> { if(variable.getSomeField1() != null){ return true; } else if(variable.getSomeField2() != null){ return false; } else { return true; } }) .map((variable -> { try{ return jsonMapper.toJson(variable); } catch (IOException e){ LOGGER.error(e); throw new RuntimeException(e); }})) .map(String::trim) .orElseThrow(() -> new RuntimeException("something went horribly wrong.")) |
上面那麼冗長程式碼塊可以使用方法替代:
Optional .ofNullable(someVariable) .map(this::findOtherObject) .filter(this::isThisOtherObjectStale) .map(this::convertToJson) .map(String::trim) .orElseThrow(() -> new RuntimeException("something went horribly wrong.")); |
相關文章
- 使用Java Optional類的最佳實踐 - oracleJavaOracle
- Java 的最佳實踐Java
- 使用 Java Native Interface 的最佳實踐Java
- Java最佳實踐Java
- Java null最佳實踐JavaNull
- Java 反射最佳實踐Java反射
- Java Map的最佳實踐 - tremblayJavaREM
- Java集合框架的最佳實踐Java框架
- Java Optional使用指南Java
- Java 斷言 Assert 使用教程與最佳實踐Java
- Optional原始碼解析與實踐原始碼
- Java最佳實踐小結 - jonathangilesJava
- Java 日誌管理最佳實踐Java
- 使用GitHub的十個最佳實踐Github
- 使用 Java 8 Optional 的正確姿勢Java
- Java 8中的Lambda表示式最佳實踐Java
- 10個精妙的Java編碼最佳實踐Java
- Java中的異常處理最佳實踐Java
- 使用Android API最佳實踐AndroidAPI
- 在Java中使用Optional效能很慢 - pkolaczkJava
- 處理Java異常的9個最佳實踐Java
- Java程式設計師的八個最佳實踐Java程式設計師
- Java異常處理的9個最佳實踐Java
- 處理Java異常的10個最佳實踐Java
- 10 個Java 編碼中微妙的最佳實踐Java
- RocketMQ的最佳實踐MQ
- mysqldump的最佳實踐MySql
- memcache的最佳實踐
- UITableViewCell使用自動佈局的“最佳實踐”UIView
- 使用Golang建立RESTful API的最佳實踐案例GolangRESTAPI
- dart系列之:集合使用最佳實踐Dart
- google Guava包RateLimiter使用最佳實踐GoGuavaMIT
- 如何正確使用Java8的Optional機制Java
- 如何在Hibernate/JPA的實體和查詢中使用Java 8 Optional?Java
- 【原創】關於JAVA複習的最佳敏捷實踐Java敏捷
- 編寫高效能 Java 程式碼的最佳實踐Java
- Java Web應用的程式碼分層最佳實踐。JavaWeb
- Java 8 之 OptionalJava