成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【修煉內(nèi)功】[Java8] 使用Optional的正確姿勢及序列化問題

Ajian / 1411人閱讀

摘要:本文已收錄修煉內(nèi)功躍遷之路的為解決空的問題帶來了很多新思路,查看源碼,實(shí)現(xiàn)非常簡單,邏輯也并不復(fù)雜。

本文已收錄【修煉內(nèi)功】躍遷之路

Java8的Optional為解決"空"的問題帶來了很多新思路,查看Optional源碼,實(shí)現(xiàn)非常簡單,邏輯也并不復(fù)雜。Stuart Marks在其一次演講中花了約1個小時的時間來講述如何正確的使用Optional (Optional - The Mother of All Bikesheds by Stuart Marks),也有人調(diào)侃道1 hour for Optional, you gotta be kidding me?.使用Optional不難,但用好Optional并不容易

Stuart Marks在演講中提到了Optional的基本作用

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.

在以往的編程模型中,對于“沒有內(nèi)容”,大多數(shù)情況需要使用null來表示,而null值總是被人忽略處理(判斷),從而在使用過程中極易引起NPE異常

Optional的出現(xiàn)并不是為了替代null,而是用來表示一個不可變的容器,它可以包含一個非null的T引用,也可以什么都不包含(不包含不等于null),非空的包含被稱作persent,而空則被稱作absent

本質(zhì)上講Optional類似于異常檢查,它迫使API用戶去關(guān)注/處理Optional中是否包含內(nèi)容,從而避免因?yàn)楹雎詎ull值檢查而導(dǎo)致的一些潛在隱患

假設(shè)有一個函數(shù)用來根據(jù)ID查詢學(xué)生信息public Student search(Long id),現(xiàn)在有一個需求,需要根據(jù)ID查詢學(xué)生姓名

public String searchName(Long id) {
    Student student = search(id);
    return student.getName();
}

注意,search函數(shù)是可能返回null的,在這種情況下searchName很有可能會拋出NPE異常

public String searchName(Long id) {
    Student student = search(id);
    return Objects.nonNull(student) ? student.getName() : "UNKNOWN";
}

除非特別明確函數(shù)的返回值不可能為null,否則一定要做null值檢查,雖然這樣寫并沒有增加太大的編碼負(fù)擔(dān),但人總歸是懶惰的,忽略檢查的情況也總是會出現(xiàn)

如果我們改造search函數(shù)返回Optional,public Optional search(Long id),再來重寫searchName函數(shù)

public String searchName(Long id) {
    Optional student = search(id);
    return student.getName();
}

這樣的代碼是編譯不過的,它會強(qiáng)制讓你去檢查search返回的值是否有內(nèi)容

public String searchName(Long id) {
    Optional student = search(id);
    return student.map(Student::getName).orElse("UNKNOWN");
}
Optional的使用可以參考其API文檔,以下內(nèi)容假設(shè)您已了解如何使用Optional

但是否就應(yīng)該消滅null,全部使用Optional來替代,回答當(dāng)然是NO,null自有它的用武之地,Optional也并不是全能的

kotlin等語言,使用?.符號來解決java中if...else…... ? ... : ...的啰嗦寫法,如上問題可以使用student?.name : null,其語義為"當(dāng)studen不為null時取其name屬性值,否則取null值",kotlin的語法只是簡化了編程方式,讓編程變得更"爽",但并沒有解決"人們?nèi)菀缀雎詎ull值檢查"的情況

Stuart Marks從5個不同的角度詳細(xì)講述了如何使用Optional,這里不一一敘述,有興趣的可以直接跳到視頻去看,下面將從Stuart Marks提到的7個Optional使用規(guī)范,來講述如何正確使用/不要濫用Optional,最后重點(diǎn)解釋一下【為什么Optional不能序列化

0x00 使用規(guī)約 Rule 1: Never, ever, user null for an Optional variable or return value.

Optional也是一個引用類型(reference type),其本身也可以賦值為null,如果我們在使用Optional的時候還要多做一層null檢查,就違背了Optional的設(shè)計(jì)初衷,所以在任何時候都不要將Optional類型的變量或返回值賦值為null,我們希望的是在遇到Optional的時候不需要關(guān)心其是否為null,只需要判斷其是否有值即可

public String searchName(Long id) {
    Optional student = search(id);
    if (Objects.isNull(student)) {
      // Optional可能為null,這嚴(yán)重違背了Optional的設(shè)計(jì)初衷
      return null;
    }
    return student.map(Student::getName).orElse("UNKNOWN");
}
Rule 2: Never user Optional.get() unless you can prove that the Optional is present.

如果Optional是absent(不包含任何值)的時候使用Optional.get(),會拋出NoSuchElementException異常,該接口的設(shè)計(jì)者也承認(rèn)其設(shè)計(jì)的不合理,之后的某個jdk版本中可能會將其標(biāo)記為@Deprecated,但還沒有計(jì)劃將其移除

public String searchName(Long id) {
    Optional student = search(id);
    // 直接使用get(),可能會拋NoSuchElementException異常
    return student.get().getName();
}

如果確實(shí)需要在Optional無內(nèi)容的時候拋出異常,也請不要使用Optional.get()方法,而要使用Optional.getOrThrow()方法,主動指定需要拋出的異常,雖然該方法并未在jdk8中設(shè)計(jì),但已經(jīng)有計(jì)劃在接下來的jdk版本中加入

Rule 3: Prefer alternatives to Optional.isPresent() and Optional.get().

如果一定要使用Optional.get(),請一定要配合isPresent(),先判斷Optional中是否有值

public String searchName(Long id) {
    Optional student = search(id);
    // 如果一定要使用Optional.get(),請一定要配合isPresent()
    return student.isPresent() ? student.get().getName() : "UNKNOWN";
}
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.

鏈?zhǔn)秸Z法可以讓代碼的處理流程看起來更加清晰,但是為了鏈?zhǔn)蕉ナ褂肙ptional的話,在某些情況下并不會顯得有多優(yōu)雅

比如,本來可以使用三目運(yùn)算

String process(String s) {
  return Objects.nonNull(s) ? s : "DEFAULT";
}

如果非要硬生生地使用鏈?zhǔn)降脑?/p>

String process(String s) {
  return Optional.ofNullable(s).orElse("DEFAULT");
}

比如,本來可以使用if判斷值的有效性

BigDecimal first = getFirstValue();
BigDecimal second = getSecondeValue();

if (Objects.nonNull(first) && Objects.nonNull(second)) {
  return first.add(second.get());
} else {
    return Objects.isNull(first) ? second : first;
}

如果非要使用鏈?zhǔn)?/p>

Optional first = getFirstValue();
Optional second = getSecondeValue();
return Stream.of(first, second)
            .filter(Optional::isPresent)
            .map(Optional::get)
            .reduce(BigDecimal::add);

或者

Optional first = getFirstValue();
Optional second = getSecondeValue();
return first.map(b -> second.map(b::add).orElse(b))
            .map(Optional::of)
            .orElse(second);

從可讀性及可維護(hù)性上來講并沒有提升,反而會帶來一絲閱讀困難,并且上文說過,Optional本身為引用類型,創(chuàng)建的Optional會進(jìn)入堆內(nèi)存,如果大量的不合理的使用Optional,也會在一定程度上影響JVM的堆內(nèi)存及內(nèi)存回收

Rule 5: If an Optional chain has a nested Optional chain, or has an intermediate result of Optional>, it"s probably too complex.

在使用Optional的時候,一定要保證Optional的簡潔性,即Optional運(yùn)算過程中所包含的類型既是最終需要的類型值,不要出現(xiàn)Optional嵌套的情況

Optional first = getFirstValue();
Optional second = getSecondeValue();

if (!first.isPresent && ! sencond.isPresent()) {
  return Optional.empty();
} else {
  return Optional.of(first.orElse(ZERO).add(second.orElse(ZERO)));
}

這樣的寫法,會對代碼的閱讀帶來很大的困擾

Rule 6: Avoid using Optional in fields, method parameters, and collections.

盡量避免將Optional用于類屬性、方法參數(shù)及集合元素中,因?yàn)橐陨先N情況,完全可以使用null值來代替Optional,沒有必要必須使用Optional,另外Optional本身為引用類型,大量使用Optional會出現(xiàn)類似(這樣描述不完全準(zhǔn)確)封箱、拆箱的操作,在一定程度上會影響JVM的堆內(nèi)存及內(nèi)存回收

Rule 7: Avoid using identity-sensitive operations on Optionals.

首先需要解釋,什么是identity-sensitive,可以參考 object identity and equality in java

identity-sensitive operations包含以下三種操作

reference equality,也就是 ==

identity hash code

synchronization

Optional的JavaDoc中有這樣一段描述

This is a value-based class; use of identity-sensitive operations (including reference equality(==), identity hash code, or synchronization) on instances of Optional may hava unpredictable results and should be avoided.

總結(jié)下來,就是要避免使用Optional的 == equals hashCode 方法

在繼續(xù)之前,需要再解釋一下什么是 value type

value type - Project Valhalla

an "object" that has no notion of identity

"code like a class, works like an int"

we eventually want to convert Optional into a value type

vale type,首先像一個類(至少從編碼角度來講),但是卻沒有類中identity的概念,運(yùn)行的時候卻又和基本類型很相似

簡單來說就是編碼的時候像類,運(yùn)行的時候像基本類型

顯然,Optional目前還不是value type,而是reference type,我們查看Optional類的equalshashCode方法,并沒有發(fā)現(xiàn)有什么特別之處,但是有計(jì)劃在接下來的某個jdk版本中將Optional定義為value type

@Override
public boolean equals(Object obj) {
    if (this == obj) {
        return true;
    }

    if (!(obj instanceof Optional)) {
        return false;
    }

    Optional other = (Optional) obj;
    return Objects.equals(value, other.value);
}

@Override
public int hashCode() {
    return Objects.hashCode(value);
}

在jdk8中使用Optional的identity-sensitive operations其實(shí)并沒有太大問題,但很難保證,在今后的后一個jdk版本中將Optional定義為value type時不會出問題,所以為了兼容jdk升級程序邏輯的正確性,請避免使用Optional的identity-sensitive operations

這也引出了Optional為什么不能序列化

0x01 序列化問題

首先,需要了解jdk中序列化的一些背景

JDK rule: forward and backword serialization compatibility across releases

If Optional were serializable today, it would be serialized as an Object

it"all always be serialized as an Object, even if eventually becomes a value type

Serialization inherently depends on object identity

Consequencds of Optional being serializable

it might prevent it from being converted into a value type in the future

deserializing and Optional might result in a "boxed" value type

首先,JDK的序列化比較特殊,需要同時向前向后兼容,如在JDK7中序列化的對象需要能夠在JDK8中反序列化,同樣在JDK8中序列化的對象需要能夠在JDK7中能夠反序列化

其次,序列化需要依賴于對象的identity

有了以上兩個序列化的前提條件,我們再來看Optional,上面已將說過,雖然目前Optional是reference type的,但其被標(biāo)記為value based class,有計(jì)劃在今后的某一個JDK版本中將其實(shí)現(xiàn)為value type

如果Optional可以序列化,那現(xiàn)在就有兩個矛盾點(diǎn)

如果Optional可以序列化,那接下來的計(jì)劃中,就沒辦法將Optional實(shí)現(xiàn)為value type,而必須是reference type

或者將value type加入identity-sensitive operations,這對于目前所有已發(fā)行的JDK版本都是相沖突的

所以,雖然現(xiàn)在Optional是reference type,但有計(jì)劃將其實(shí)現(xiàn)為value type,考慮到JDK序列化的向前向后兼容性,從一開始就將Optional定為不可序列化,應(yīng)該是最合適的方案了

如果真的有在類屬性上使用Optional的需求怎么辦?這里有兩個替代方案/討論可以參考

Optional Pragmatic Approach

Nothing is better than the Optional type

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/74204.html

相關(guān)文章

  • 修煉內(nèi)功】[Java8] Lambda究竟是不是匿名類語法糖

    摘要:本文已收錄修煉內(nèi)功躍遷之路初次接觸的時候感覺表達(dá)式很神奇表達(dá)式帶來的編程新思路,但又總感覺它就是匿名類或者內(nèi)部類的語法糖而已,只是語法上更為簡潔罷了,如同以下的代碼匿名類內(nèi)部類編譯后會產(chǎn)生三個文件雖然從使用效果來看,與匿名類或者內(nèi)部類有相 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbui4o?w=800&h=600)...

    ?xiaoxiao, 評論0 收藏0
  • 修煉內(nèi)功】[JVM] 虛擬機(jī)視角方法調(diào)用

    摘要:本文已收錄修煉內(nèi)功躍遷之路我們寫的方法在被編譯為文件后是如何被虛擬機(jī)執(zhí)行的對于重寫或者重載的方法,是在編譯階段就確定具體方法的么如果不是,虛擬機(jī)在運(yùn)行時又是如何確定具體方法的方法調(diào)用不等于方法執(zhí)行,一切方法調(diào)用在文件中都只是常量池中的符號引 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbuesq?w=2114&h=12...

    shevy 評論0 收藏0
  • 修煉內(nèi)功】[JVM] 淺談虛擬機(jī)內(nèi)存模型

    摘要:也正是因此,一旦出現(xiàn)內(nèi)存泄漏或溢出問題,如果不了解的內(nèi)存管理原理,那么將會對問題的排查帶來極大的困難。 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbsP9I?w=1024&h=580); 不論做技術(shù)還是做業(yè)務(wù),對于Java開發(fā)人員來講,理解JVM各種原理的重要性不必再多言 對于C/C++而言,可以輕易地操作任意地址的...

    sanyang 評論0 收藏0
  • 修煉內(nèi)功】[JVM] 類文件結(jié)構(gòu)

    摘要:本文已收錄修煉內(nèi)功躍遷之路學(xué)習(xí)語言的時候,需要在不同的目標(biāo)操作系統(tǒng)上或者使用交叉編譯環(huán)境,使用正確的指令集編譯成對應(yīng)操作系統(tǒng)可運(yùn)行的執(zhí)行文件,才可以在相應(yīng)的系統(tǒng)上運(yùn)行,如果使用操作系統(tǒng)差異性的庫或者接口,還需要針對不同的系統(tǒng)做不同的處理宏的 本文已收錄【修煉內(nèi)功】躍遷之路 showImg(https://segmentfault.com/img/bVbtpPd?w=2065&h=11...

    Eminjannn 評論0 收藏0
  • Java8實(shí)用技能

    大概一年多之前,我對java8的理解還僅限一些只言片語的文章之上,后來出于對函數(shù)式編程的興趣,買了本參考書看了一遍,然后放在了書架上,后來,當(dāng)我接手大客戶應(yīng)用的開發(fā)工作之后,java8的一些工具,對我的效率有了不小的提升,因此想記錄一下java8的一些常用場景,我希望這會成為一個小字典,能讓我免于頻繁翻書,但是總能找到自己想找的知識。 用于舉例的model: @Data public class ...

    microcosm1994 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<