<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    Java8 Optional 最佳實(shí)踐

    共 7523字,需瀏覽 16分鐘

     ·

    2021-07-27 02:55

    作者 | ES_her0

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

    很多公眾號其實(shí)都發(fā)過 Optional 的文章, 但大多文章都是介紹了 Optional 的 API 用法,卻沒有給出怎么正確的使用 Optional,這可能會誤導(dǎo)一部分小白使用者,私以為,在項(xiàng)目中一知半解的使用 Optional,我更愿意看到老老實(shí)實(shí)的 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帶來的錯誤,我們提供了一個可以明確表示空值的有限的機(jī)制。

    基礎(chǔ)理解

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

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

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

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

    // 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 的非空類型是他們大肆宣傳的"賣點(diǎn)"之一,通過var param!!在使用它的地方做強(qiáng)制的空檢查,否則無法通過編譯,最大程度上減少了 NPE。其實(shí)在我看來,Optional的方式更加優(yōu)雅和靈活。同時,Optional也可能會帶來一些誤解。

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

    Bad Practice

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

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

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

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

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

    //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)。這種可能會出現(xiàn)在那種所謂的著急上線,著急交付,對Optional也不是很熟悉,直接就用了。這里多說一句,可能有人會反問了:甲方/業(yè)務(wù)著急,需求又多,哪有時間給他去做優(yōu)化???因?yàn)槲以诂F(xiàn)實(shí)工作中遇到過,但這兩者并不矛盾,因?yàn)榇a行數(shù)上差別并不大,只要自己平時保持學(xué)習(xí),都是信手拈來的東西。

    4. 使用在 POJO 中

    估計(jì)很少有人這么用:

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

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

    5. 使用在注入的屬性中

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

    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)該終止并報(bào)異常,并不是無聲無息,讓其看起來什么問題都沒有。

    Best and Pragmatic Practice

    API

    在說最佳實(shí)踐前,讓我們來看一下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中包裝的值,但不同的是當(dāng)取不到值時,返回你指定的 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實(shí)例屬性
    • 不要在任何 setter 或者構(gòu)造方法中使用Optional
    • Optional屬于返回類型,在業(yè)務(wù)返回值或者遠(yuǎn)程調(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其實(shí)沒多大區(qū)別,只是在表達(dá)和抽象上有所改善。

    其次,為什么不推薦orElse()呢?因?yàn)?code style="font-size: 14px;font-family: "Operator Mono", Consolas, Monaco, Menlo, monospace;padding: 2px 4px;border-radius: 4px;margin-right: 2px;margin-left: 2px;color: rgb(53, 179, 120);background-color: rgba(27, 31, 35, 0.05);word-break: break-all;">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()方法是一個遠(yuǎn)程調(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ù)場景比較合適,例如沒有從上游獲取到用戶信息,下面的所有操作都無法進(jìn)行,那此時就應(yīng)該拋出異常。正常的寫法是先判空,再手動 throw 異常,現(xiàn)在可以集成為一行:

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

    這點(diǎn)沒有性能上的優(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)具備了完整的空表達(dá),再去包裝一層Optional也是徒增復(fù)雜,收益甚微。例如,map 已經(jīng)有了getOrDefault()這樣的類似orElse()的 API 了。

    總結(jié)

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

    往期推薦

    令人笑噴的56個代碼注釋,你寫過多少?

    還在用 Random生成隨機(jī)數(shù)?試試 ThreadLocalRandom,超好用!

    消息冪等(去重)通用解決方案,真頂!

    最強(qiáng)代碼生成器平臺,殺瘋了!

    Spring為什么建議使用構(gòu)造器來注入?



    喜歡本文歡迎轉(zhuǎn)發(fā),關(guān)注我訂閱更多精彩

    關(guān)注我回復(fù)「加群」,加入Spring技術(shù)交流群

    瀏覽 47
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評論
    圖片
    表情
    推薦
    點(diǎn)贊
    評論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    欧美操逼A片 | 欧美成人性爱精品 | 大奶熟女性爱视频 | 轻轻操免费视频 | 夜夜嗨AV无码精品 |