<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java8 Optional 最佳實踐!

          共 7558字,需瀏覽 16分鐘

           ·

          2021-09-12 18:37

          作者:ES_her0

          來源:xie.infoq.cn/article/e3d1f0f4f095397c44812a5be

          很多公眾號其實都發(fā)過 Optional 的文章, 但大多文章都是介紹了 Optional 的 API 用法,卻沒有給出怎么正確的使用 Optional,這可能會誤導(dǎo)一部分小白使用者,私以為,在項目中一知半解的使用 Optional,我更愿意看到老老實實的 null 判斷。今天我給大家分享的這篇文章,便是 Java Optional 的一些 Best Practise 和一些反面的 Bad Practice,以供大家參考。

          來自作者的說明

          首先我們來看一下Optional的作者 Brian Goetz 對這個 API 的說明:

          Our intention was to provide a limited mechanism for library method return types where there needed to be a clear way to represent "no result", and using null for such was overwhelmingly likely to cause errors.

          大意為,為了避免null帶來的錯誤,我們提供了一個可以明確表示空值的有限的機制。

          基礎(chǔ)理解

          首先,Optional是一個容器,用于放置可能為空的值,它可以合理而優(yōu)雅的處理null。眾所周知,null在編程歷史上極具話題性,號稱是計算機歷史上最嚴重的錯誤,感興趣可以讀一下這篇文章:THE WORST MISTAKE OF COMPUTER SCIENCE,這里暫且不做過多討論。在 Java 1.8 之前的版本,沒有可以用于表示null官方 API,如果你足夠的謹慎,你可能需要常常在代碼中做如下的判斷:

          if (null != user) {
              //doing something
          }
          if (StringUtil.isEmpty(string)) {
              //doing something
          }

          確實,返回值是null的情況太多了,一不小心,就會產(chǎn)生 NPE,接踵而來的就是應(yīng)用運行終止,產(chǎn)品抱怨,用戶投訴。

          1.8 之后,jdk 新增了Optional來表示空結(jié)果。其實本質(zhì)上什么也沒變,只是增加了一個表達方式。Optional表示空的靜態(tài)方法為Optional.empty(),跟null有什么本質(zhì)區(qū)別嗎?其實沒有。翻看它的實現(xiàn),Optional中的 value 就是null,只不過包了一層Optional,所以說它其實是個容器。用之后的代碼可能長這樣:

          // 1
          Optional<User> optionalUser = RemoteService.getUser();
          if (!optionalUser.isPresent()) {
             //doing something 
          }
          User user = optionalUser.get();

          // 2
          User user = optionalUser.get().orElse(new User());

          看起來,好像比之前好了一些,至少看起來沒那么笨。但如果采用寫法 1,好像更啰嗦了。

          如果你對 kotlin 稍有了解,kotlin 的非空類型是他們大肆宣傳的"賣點"之一,通過var param!!在使用它的地方做強制的空檢查,否則無法通過編譯,最大程度上減少了 NPE。其實在我看來,Optional的方式更加優(yōu)雅和靈活。同時,Optional也可能會帶來一些誤解。

          下面先說一些在我看來不合適的使用方式:

          Bad Practice

          1. 直接使用 isPresent() 進行 if 檢查

          這個直接參考上面的例子,用if判斷和 1.8 之前的寫法并沒有什么區(qū)別,反而返回值包了一層Optional,增加了代碼的復(fù)雜性,沒有帶來任何實質(zhì)的收益。其實isPresent()一般用于流處理的結(jié)尾,用于判斷是否符合條件。

          list.stream()
              .filer(x -> Objects.equals(x,param))
              .findFirst()
              .isPresent()

          2. 在方法參數(shù)中使用 Optional

          我們用一個東西之前得想明白,這東西是為解決什么問題而誕生的。Optional直白一點說就是為了表達可空性,如果方法參數(shù)可以為空,為何不重載呢?包括使用構(gòu)造函數(shù)也一樣。重載的業(yè)務(wù)表達更加清晰直觀。

          //don't write method like this
          public void getUser(long uid,Optional<Type> userType);

          //use Overload
          public void getUser(long uid) {
              getUser(uid,null);
          }
          public void getUser(long uid,UserType userType) {
              //doing something
          }

          3. 直接使用 Optional.get

          Optional不會幫你做任何的空判斷或者異常處理,如果直接在代碼中使用Optional.get()和不做任何空判斷一樣,十分危險。這種可能會出現(xiàn)在那種所謂的著急上線,著急交付,對Optional也不是很熟悉,直接就用了。這里多說一句,可能有人會反問了:甲方/業(yè)務(wù)著急,需求又多,哪有時間給他去做優(yōu)化???因為我在現(xiàn)實工作中遇到過,但這兩者并不矛盾,因為代碼行數(shù)上差別并不大,只要自己平時保持學習,都是信手拈來的東西。

          4. 使用在 POJO 中

          估計很少有人這么用:

          public class User {
              private int age;
              private String name;
              private Optional<String> address;
          }

          這樣的寫法將會給序列化帶來麻煩,Optional本身并沒有實現(xiàn)序列化,現(xiàn)有的 JSON 序列化框架也沒有對此提供支持的。

          5. 使用在注入的屬性中

          這種寫法估計用的人會更少,但不排除有腦洞的。

          public class CommonService {
              private Optional<UserService> userService;
              
              public User getUser(String name) {
                  return userService.ifPresent(u -> u.findByName(name));
              }
          }

          首先依賴注入大多在 spring 的框架之下,直接使用@Autowired很方便。但如果使用以上的寫法,如果userService set 失敗了,程序就應(yīng)該終止并報異常,并不是無聲無息,讓其看起來什么問題都沒有。

          Best and Pragmatic Practice

          API

          在說最佳實踐前,讓我們來看一下Optional都提供了哪些常用 API。

          1. empty()

          返回一個Optional容器對象,而不是 null。建議常用????

          2. of(T value)

          創(chuàng)建一個Optional對象,如果 value 是 null,則拋出 NPE。不建議用??

          3. ofNullable(T value)

          同上,創(chuàng)建一個Optional對象,但 value 為空時返回Optional.empty()。推薦使用?????

          4. get()

          返回Optional中包裝的值,在判空之前,千萬不要直接使用!盡量別用!?

          5. orElse(T other)

          同樣是返回Optional中包裝的值,但不同的是當取不到值時,返回你指定的 default。看似很好,但不建議用??

          6. orElseGet(Supplier<? extends T> other)

          同樣是返回Optional中包裝的值,取不到值時,返回你指定的 default。看似和 5 一樣,但推薦使用?????

          7. orElseThrow(Supplier<? extends X> exceptionSupplier)

          返回Optional中包裝的值,取不到值時拋出指定的異常。阻塞性業(yè)務(wù)場景推薦使用????

          8. isPresent()

          判斷Optional中是否有值,返回 boolean,某些情況下很有用,但盡量不要用在 if 判斷體中。可以用???

          9. ifPresent(Consumer<? super T> consumer)

          判斷Optional中是否有值,有值則執(zhí)行 consumer,否則什么都不干。日常情況下請使用這個????

          TIPS

          首先是一些基本原則:

          • 不要聲明任何Optional實例屬性
          • 不要在任何 setter 或者構(gòu)造方法中使用Optional
          • Optional屬于返回類型,在業(yè)務(wù)返回值或者遠程調(diào)用中使用
          1. 業(yè)務(wù)上需要空值時,不要直接返回 null,使用Optional.empty()
          public Optional<User> getUser(String name) {
              if (StringUtil.isNotEmpty(name)) {
                  return RemoteService.getUser(name);
              } 
              return Optional.empty();
          }
          2. 使用 orElseGet()

          獲取 value 有三種方式:get() orElse() orElseGet()。這里推薦在需要用到的地方只用 orElseGet()

          首先,get()不能直接使用,需要結(jié)合判空使用。這和!=null其實沒多大區(qū)別,只是在表達和抽象上有所改善。

          其次,為什么不推薦orElse()呢?因為orElse()無論如何都會執(zhí)行括號中的內(nèi)容, orElseGet()只在主體 value 是空時執(zhí)行,下面看個例子:

          public String getName() {
              System.out.print("method called");
          }

          String name1 = Optional.of("String").orElse(getName()); //output: method called
          String name2 = Optional.of("String").orElseGet(() -> getName()); //output:

          如果上面的例子getName()方法是一個遠程調(diào)用,或者涉及大量的文件 IO,代價可想而知。

          但 orElse()就一無是處嗎?并不是。orElseGet()需要構(gòu)建一個Supplier,如果只是簡單的返回一個靜態(tài)資源、字符串等等,直接返回靜態(tài)資源即可。

          public static final String USER_STATUS = "UNKNOWN";
          ...
          public String findUserStatus(long id) {
              Optional<String> status = ... ; // 
              return status.orElse(USER_STATUS);
          }

          //不要這么寫
          public String findUserStatus(long id) {
              Optional<String> status = ... ; // 
              return status.orElse("UNKNOWN");//這樣每次都會新建一個String對象
          }
          3. 使用 orElseThrow()

          這個針對阻塞性的業(yè)務(wù)場景比較合適,例如沒有從上游獲取到用戶信息,下面的所有操作都無法進行,那此時就應(yīng)該拋出異常。正常的寫法是先判空,再手動 throw 異常,現(xiàn)在可以集成為一行:

          public String findUser(long id) {
              Optional<User> user = remoteService.getUserById(id) ;
              return user.orElseThrow(IllegalStateException::new);
          }
          4. 不為空則執(zhí)行時,使用 ifPresent()

          這點沒有性能上的優(yōu)勢,但可以使代碼更簡潔:

          //之前是這樣的
          if (status.isPresent()) {
              System.out.println("Status: " + status.get());
          }

          //現(xiàn)在
          status.ifPresent(System.out::println);
          5. 不要濫用

          有些簡單明了的方法,完全沒必要增加Optional來增加復(fù)雜性。

          public String fetchStatus() {
              String status = getStatus() ;
              return Optional.ofNullable(status).orElse("PENDING");
          }

          //判斷一個簡單的狀態(tài)而已
          public String fetchStatus() {
              String status = ... ;
              return status == null ? "PENDING" : status;
          }

          首先,null 可以作為集合的元素之一,它并不是非法的;其次,集合類型本身已經(jīng)具備了完整的空表達,再去包裝一層Optional也是徒增復(fù)雜,收益甚微。例如,map 已經(jīng)有了getOrDefault()這樣的類似orElse()的 API 了。

          總結(jié)

          Optional的出現(xiàn)使 Java 對 null 的表達能力更近了一步,好馬配好鞍,合理使用可以避免大量的 NPE,節(jié)省大量的人力物力。以上內(nèi)容也是本人查詢了很多資料,邊學邊寫的產(chǎn)出,如有錯漏之處,還請不吝指教。

          程序汪資料鏈接

          程序汪接的7個私活都在這里,經(jīng)驗整理

          Java項目分享  最新整理全集,找項目不累啦 04版

          堪稱神級的Spring Boot手冊,從基礎(chǔ)入門到實戰(zhàn)進階

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動總結(jié)的設(shè)計模式 PDF 火了,完整版開放下載!

          歡迎添加程序汪個人微信 itwang008  進粉絲群或圍觀朋友圈

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲中文在线视频 | AV无码免费一区二区三区不卡 | 不要播放器的av网站 | 人人弄人人 | 北条麻妃中文字幕在线观看 |