差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

java:effective_java:methods:return_optionals_judiciously [2021/07/05 09:25]
tony [Rule#6: Avoid using Optional in fields, method parameters, and collections.]
java:effective_java:methods:return_optionals_judiciously [2023/06/25 09:48]
行 1: 行 1:
-{{tag>​java effective_java}} 
-====== Effective Java - Return optionals judiciously ====== 
-===== Introduction ===== 
-這個item主要告訴你使用Java 8推出的Optional的注意事項。本篇文章我以Java開發人員Stuart Marks[[https://​www.youtube.com/​watch?​v=fBYhtvY19xA&​t=1833s|演講內容]]與[[https://​stuartmarks.files.wordpress.com/​2017/​10/​optionalmotherofallbikesheds-devoxxus2017.pdf|投影片]]中所提到的Rules去整理Effective Java與網路上的內容。 
-==== Rule#1: Never, ever, use null for an Optional variable or return value. ==== 
-使用Optional的目的,是希望提供一個機制讓method回傳結果能夠表達"​no result"​,而不是透過null,因為null造成了很多的bug。以下為原文:​ 
-<​code>​ 
-Optional is intended to provide a limited mechanism for library method return types where there is a clear need to represent "no result,"​ and where using null for that is overwhelmingly likely to cause errors. ​ 
-                                                                       - From Stuart Marks' presentation 
-</​code>​ 
-除此之外,也可以參考Java Language Architect - Brian Goetz的[[https://​stackoverflow.com/​questions/​26327957/​should-java-8-getters-return-optional-type|說法]]。 
-因此,當你讓Optional變數塞了null,或者是直接回傳null,就是脫褲子放屁;而在我們的production code中,還真的有這種code。\\ 
-\\ 
-另外,Effective Java有提到適合使用Optional當method回傳值的使用時機:​ 是你的client需要針對空值做特別處理的時候。 
-\\ 
-==== Rule#2: Never use Optional.get() unless you can prove that the Optional is present. ==== 
-第二個rule是要告訴你別直接使用Optional.get()去取得值,除非你確定它一定有值,如果使用sonarlint,會提示你至少要寫成這樣:​ 
-<code java> 
-Optional<​User>​ searchResult = userRepository.findUser("​123"​);​ 
-    if( searchResult.isPresent() ) 
-        dump(searchResult.get());​ 
-</​code>​ 
-如果沒先判斷就直接取值,你就會收到java.util.NoSuchElementException:​ No value present。我們最近也因為有人違反這個rule而產生bug。 
-\\ 
-==== Rule#3: Prefer alternatives APIs over Optional.isPresent() and Optional.get(). ==== 
-延續Rule#​2,Optional提供了不少alternative API讓你可以不需要先透過isPresent()判斷,再做後續動作,目的是要讓code更簡潔。以Rule#​2中的例子來說,可以寫成這樣:​ 
-<code java> 
-Optional<​User>​ searchResult = userRepository.findUser("​123"​);​ 
-searchResult.ifPresent(TestOptional::​dump);​ 
-</​code>​ 
-透過ifPresent再傳入Consumer method,就可以讓code更簡潔;除此之外,orElse、orElseGet、orElseThrow也是蠻常使用的。這個[[https://​dzone.com/​articles/​using-optional-correctly-is-not-optional|連結]]敘述了蠻多alternative APIs的使用方法,可以仔細閱讀。 
-\\ 
-==== Rule#4: It’s generally a bad idea to create an Optional for the specific purpose of chaining methods from it to get a value. ==== 
-這個Rule是告訴你,不要為了使用Optional或者是為了fluent interface而使用它,除非你是要回傳Optional:​ 
-<code java> 
-// BAD  
-String process(String s) {  
-    return Optional.ofNullable(s).orElseGet(this::​getDefault);​  
-}  
-// GOOD  
-String process(String s) {  
-    return (s != null) ? s : getDefault();​  
-} 
-</​code>​ 
-除了考量到效能以外,還要判斷使用Optional以後,code是不是真的比較好閱讀。 
-\\ 
-==== Rule#5: If an Optional chain has a nested Optional chain, or has an intermediate result of Optional<​Optional<​T>>,​ it’s probably too complex. ​ ==== 
-這個Rule是說Optional內不要再包Optional,我想這會讓client code變得很複雜。 
-\\ 
- 
-==== Rule#6: Avoid using Optional in fields, method parameters, and collections. ==== 
-再來是我研究最久的Rule,建議Optional避免使用在fields、method parameters與collections。\\ 
-\\ 
-首先談的是collection,在collection的情況下使用Optional,其實是不必要的。主要原因有以下:​ 
-  * 使用Optional後,反而額外增加client要多考慮有值與沒有值的情況。 
-  * collection本身就可以用來代表有值或沒有值的情況,可以參考Effective Java Item#54: [[java:​effective_java:​methods:​return_empty_collections_or_arrays_not_nulls|link]]。 
-我目前想的到有可能會把collection用Optional包裝的情況,應該只有用來代表使用者是否有輸入值的情境吧?​ 當我需要去識別「使用者有否輸入」,就無法使用empty collection去代表這件事情,除非再增加其它的flag變數。\\ 
-\\ 
-不要將Optional使用於method parameter的原因,主要是會變成強迫client要轉Optional物件,增加API使用的不便性。\\ 
-\\ 
-最後是最有爭議的,是否要將Optional使用於field。反對者主要有以下論述:​ 
-  * Optional不支援序列化,這個是主因。(列為anti-pattern的主因) 
-  * 多消耗4倍記憶體空間。(演講中有提及) 
-  * Optional是一個box,包覆另外一個value物件,這會增加額外的GC開銷。(演講中有提及) 
-針對以上三點,序列化問題可以透過Guava Optional或Vivr Optional去解決,但也要搭配[[java:​jackson:​deepclone|Jackson Module]]或Gson的TypeAdapter做對應的處理;多消耗4倍記憶體空間在現在時代已經不是嚴重的事情(Stuart Marks的演講有提及);在Java8的情況下,第三點就無法避免了。\\ 
-目前已知的是,JDK的[[https://​openjdk.java.net/​projects/​valhalla/​|Valhalla]]專案希望能夠將Optional變成Value Type去避免這個問題,也因為有這個計畫,導致目前無法讓Optional去實作Serializable(會產生向下相容問題)。JodaTime作者認為如果這天來臨,內建的Optional應可以用於任何地方:​\\ 
-{{:​java:​effective_java:​methods:​effectivejava_item55_optional_with_serializable.png|}}\\ 
-JodaTime作者認為比較好的寫法:​ 
-<code java> 
-private String mail; // optional 
-public Optional<​String>​ getMail(){ 
-    return Optional.ofNullable(mail);​ 
- 
-public void setMail(String mail){ 
-    this.mail = mail; 
-} 
-</​code>​ 
-\\ 
-支持者的論點,主要有兩點:​ 
-  * 宣告於欄位代表著非必要欄位,語意表達比使用null加註解清楚,更何況大家都不愛寫註解。此外,這個Optional支持者反對null,不偏好JodaTime作者的方法。[[https://​nipafx.dev/​stephen-colebourne-java-optional-strict-approach/​|link]] 
-  * Effective Java Item 55有提及,適用於物件有許多非必要欄位的情況,像是部分更新的UseCaseInput model。如果按照Stuart Marks的建議使用Null Object Pattern,那將會因此建立許多Sub Classes,非常不方便。 
-  * 在使用Lombok的情況下,有人會想將Optional宣告為field;但被Lombok開發人員打槍,因為他愛null。[[https://​stackoverflow.com/​questions/​31670785/​optional-in-lombok|link]] 
-支持者與反對者比較寫法如下,我認為trade-off就在對於效能、語意、序列化與框架的使用上了,單元測試成本幾乎是一樣的:​ 
-<code java> 
-private String mail; // optional 
-private Optional<​String>​ address = Optional.empty();​ 
-    ​ 
-public Optional<​String>​ getMail(){ 
-    return Optional.ofNullable(mail);​ 
-} 
-    ​ 
-public void setMail(String mail){ ​ 
-    Objects.requireNonNull(mail,​ "Mail should not be null."​);​ 
-    this.mail = mail; 
-} 
-    ​ 
-public Optional<​String>​ getAddress(){ 
-    return address; 
-} 
-    ​ 
-public void setAddress(String address) { 
-    this.address = Optional.of(address);​ 
-} 
-</​code>​ 
-在Java 8的情況下,我提供以下主要的trade-off比較給大家參考:​ 
-  * 允許Optional於欄位讓語意清楚:​ 效能較差、有序列化需求要特別套其它library。 
-  * 允許null於欄位並僅在回傳值使用Optional:​ 僅有語意問題,null控制好就無大礙。 
-==== Rule#7: Avoid using identity-sensitive operations on Optionals. For example don’t use reference equality == with Optionals. ==== 
-這個Rule是告訴大家不要把Optional的物件使用==去操作,要用equals;除了這個以外,也不要把它拿來synchronized。主要原因是Optional本質上是一個包裝物件的box,未來有很大的機會變成Value Type,參考此[[https://​openjdk.java.net/​projects/​valhalla/​|link]]。 
-===== 其它 ===== 
-==== 反對Optional ==== 
-在演講中有提到有些反對Optional的[[https://​homes.cs.washington.edu/​~mernst/​advice/​nothing-is-better-than-optional.html|論述]],有興趣的人可以自行參考。 
-==== Effective Java還有提及的 ==== 
-  - 為了減少boxing與unboxing的開銷,int、long、double可以使用OptionalInt、OptionalLong、OptionalDouble。 
-  - 不建議使用在map的key上,因為Optional包含著有值與沒有值的情況,使用在map上只是增添不必要的複雜度。最後再次強調:​ it is almost never appropriate to use an optional as a key, value, or element in a collection or array. 
-===== Note ===== 
-Effective Java第三版Item 55。 
-===== Reference ===== 
-  * Effective Java, 3/e 
-  * [[https://​dzone.com/​articles/​using-optional-correctly-is-not-optional|26 Reasons Why Using Optional Correctly Is Not Optional]] 
-  * [[https://​blog.joda.org/​2014/​11/​optional-in-java-se-8.html|Optional in Java SE 8]] 
-  * [[https://​dzone.com/​articles/​optional-anti-patterns|Optional Anti-Patterns]] 
-  * [[https://​youtu.be/​fBYhtvY19xA?​t=1833|Optional by Stuart Marks]] 
-  * [[https://​stackoverflow.com/​questions/​26327957/​should-java-8-getters-return-optional-type|Should Java 8 getters return optional type?]] 
-=====    ===== 
----- 
-\\ 
-~~DISQUS~~