Java 8 | Optional

我想在搬磚的過程中,大家一定都遇到過 NPE (NullPointerException) 吧。當(dāng)我們嘗試使用一個還沒有初始化或者初始化為 null 的變量時,就會出現(xiàn) NPE。Java 8 添加了 Optional 來嘗試解決這個問題。接下來讓我們來了解一下這個類吧操灿。

空(Null) 到底是什么類型 ?

在 Java 中锯仪,我們使用引用來訪問一個對象實(shí)例,當(dāng)這個引用不知道指向那個對象實(shí)例時趾盐,我們通常都將它設(shè)置為 null庶喜。

平時在搬磚過程中,null 的使用是那么的普遍,以致于我們很少會去考慮到它芬失。如果沒有特殊指定峻村,對象的實(shí)例變量通常都自動初始化為 null锚扎,當(dāng)我們不知道給這個引用賦什么值的時候芍秆,通常也會將 null 賦值給它荆虱。

那么 空(Null) 到底是什么類型呢骑脱?

僅供參考菜枷,在 Java 中,空(Null) 其實(shí)也是一種類型叁丧,只不過它很特殊犁跪,我們只能將 null 這個字面量賦值給這種類型椿息。而且這種類型不像其他的 Java 類型,null 可以賦值給任何其它的 Java 類型而不會出現(xiàn)錯誤坷衍。

返回 null 有什么問題嗎 ?

在開發(fā)過程中寝优,我們經(jīng)常會使用第三方提供的 API 接口轿腺,但是這些 API 接口的返回值可能為 null魄健,返回結(jié)果的說明可能在 API 文檔中已經(jīng)有說明,但是當(dāng)我們開發(fā)的時候润匙,由于各種原因迁杨,沒有仔細(xì)閱讀文檔(甚至就沒有讀過文檔)钻心,忘記了處理返回值為 null 的情況,那么這就可能會存在 NPE 問題铅协。而且這種現(xiàn)象在平時開發(fā)過程中是經(jīng)常發(fā)生的捷沸。

那么怎么有效的解決這個問題呢?一個比較好的辦法就是對返回值都進(jìn)行初始化狐史,但是初始化的值不能是 null(例子就是痒给,如果方法返回值是一個 List,那么我們可以返回一個 Collections.emptyList())骏全,這樣就不會出現(xiàn) NPE 了苍柏,你們說是不是這個道理?

嗯姜贡,理想很美好试吁,現(xiàn)實(shí)很骨感。在實(shí)際中楼咳,我們有可能找不到這么一個不是 null 的初始化值熄捍,那么該怎么辦呢?于是 Optional 出現(xiàn)了母怜。

Java 8 的 Optional 怎么解決 NPE 呢治唤?

Optional 就是一個引用為空的非空值。Optional 可能包含一個非空的引用糙申,也可能不會包含什么宾添,記住永遠(yuǎn)不要說 Optional 包含 null。

Optional<Integer> canBeEmpty1 = Optional.of(5);
canBeEmpty1.isPresent();                    // returns true
canBeEmpty1.get();                          // returns 5

Optional<Integer> canBeEmpty2 = Optional.empty();
canBeEmpty2.isPresent();                    // returns false

我們可以把 Optional 當(dāng)做一個單值容器柜裸,它里面可能會裝一個值缕陕,也可能什么都不裝。

需要說明的是 Optional 類并不是替代 null 的疙挺,加入 Optional 類是為了可以設(shè)計出更容易理解的 API 接口扛邑。比如

public User getUserFromDB(long id);

我們可能調(diào)用這個接口后,直接就把 User 這個引用拿來使用了铐然,忘記判斷引用可能為空的情況蔬崩。那么如果這個接口是這樣的

public Optional<User> getUserFromDB(long id);

當(dāng)我們看到這個就接口的時候就會意識到恶座,這個接口返回的結(jié)果可能會為空,這樣我們就會先對這個值做判斷沥阳,然后再使用它跨琳。是不是一眼就明白了。

下面我們舉一些使用 Optional 的例子

a) 創(chuàng)建 Optional 對象

有 3 種方法來創(chuàng)建 Optional 對象桐罕。

i) 使用 Optional.empty() 創(chuàng)建沒有值的 Optional 容器脉让。

Optional<Integer> possible = Optional.empty();

ii) 使用 Optional.of() 創(chuàng)建一個裝有值的 Optional 容器。如果你傳一個 null 給 Optional.of() 方法功炮,那么會拋出 NullPointerException溅潜。

Optional<Integer> possible = Optional.of(5);

iii) 使用 Optional.ofNullable() 創(chuàng)建一個可能沒有值的 Optional 容器。

Optional<Integer> possible = Optional.ofNullable(null);
//or
Optional<Integer> possible = Optional.ofNullable(5);

b) Optional 容器有值處理

當(dāng)獲得了 Optional 對象薪伏,首先應(yīng)該檢查一下 Optional 容器中是否有值滚澜。

Optional<Integer> possible = Optional.of(5);
possible.ifPresent(System.out::println);

當(dāng)然,上面這段代碼也可以寫成下面?zhèn)鹘y(tǒng)的樣子

if(possible.isPresent()){
    System.out.println(possible.get());
}

但是這并不是推薦的寫法嫁怀,要不然 Java 8 的 lambda 表達(dá)式有何用设捐?

c) Optional 容器無值處理

當(dāng)返回值不存在的時候,通常情況下我們會返回一個默認(rèn)值眶掌。一般我們會使用三元運(yùn)算符來處理挡育,但是使用 Optional 我們可以這樣

//Assume this value has returned from a method
Optional<Company> companyOptional = Optional.empty();
 
//Now check optional; if value is present then return it,
//else create a new Company object and retur it
Company company = companyOptional.orElse(new Company());
 
//OR you can throw an exception as well
Company company = companyOptional.orElseThrow(IllegalStateException::new);

d) 通過 filter 方法做過濾

通常我們回去一個結(jié)果需要對這個結(jié)果做一些過濾巴碗,篩選出符合我們要求的結(jié)果朴爬。

Optional<Company> companyOptional = Optional.empty();
companyOptional.filter(department -> "Finance".equals(department.getName())
                    .ifPresent(() -> System.out.println("Finance is present"));

filter 方法接受一個 predicate 參數(shù)。如果 Optional 有值而且滿足 predicate橡淆,filter 方法就會返回這個值召噩,否則就會返回一個無值的 Optional。

哇哦逸爵,現(xiàn)在我們已經(jīng)不用在代碼中寫各式各樣的 null 值判斷邏輯了具滴,是不是看著更簡潔了呢。

Optional 內(nèi)部是怎么處理的呢?

看看 Optional.java师倔,Optional 持有的值定義

/**
 * If non-null, the value; if null, indicates no value is present
 */
private final T value;

如果值為空构韵,使用的是一個靜態(tài)變量表示

/**
 * Common instance for {@code empty()}.
 */
private static final Optional<?> EMPTY = new Optional<>();

Optional 類的構(gòu)造函數(shù)都是私有的,這樣就確保只能使用上面提供的3種創(chuàng)建 Optional 實(shí)例趋艘。

當(dāng)創(chuàng)建一個有值的 Optional 實(shí)例時疲恢,會對傳入的 value 進(jìn)行判斷

this.value = Objects.requireNonNull(value);

當(dāng)從 Optional 獲取值得時候,如果 value 為 null 會拋出 NoSuchElementException 異常

public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

同樣的瓷胧,Optional 類中的其他方法都是圍繞著這個 value 進(jìn)行的显拳。

Opti0nal 嘗試解決的

Optional 類嘗試解決 Java 中 NPE 問題。通過 Optional 類設(shè)計出更容易理解的 API搓萧,如果 Optional 類從開始就被設(shè)計出來杂数,我相信在現(xiàn)在的類庫宛畦、應(yīng)用中對 null 值的處理會更加合理。

通過使用Optional 類揍移,這回強(qiáng)迫開發(fā)者思考為空的異常情況次和。當(dāng)編寫 API 的時候,如果返回值是Optional 類羊精,編寫 API 的開發(fā)者不得不考慮為空的這種情況斯够,因?yàn)椴荒苤苯臃祷?null 了,要不然編譯也不會通過的喧锦。

Opti0nal 不能解決的

正如前面說到的读规,Opti0nal 并不能代替所有 null 的情況。當(dāng)函數(shù)的入?yún)⒂?null 的時候燃少,并不推薦使用 Opti0nal束亏。你想想當(dāng)你調(diào)用一個接口的時候,我們還得封裝一個成 Opti0nal 對象給它阵具,是不是有點(diǎn)麻煩碍遍。

為了更優(yōu)雅的使用 Opti0nal 類,下面情況最好不要使用

  1. 在數(shù)據(jù)模型層(Opti0nal 不可序列化)
  2. 在 DTO 中(Opti0nal 不可序列化)
  3. 在方法的入?yún)⒅?/li>
  4. 在構(gòu)造函數(shù)中

在什么地方使用 Opti0nal 呢阳液?

Opti0nal 應(yīng)該作為可能返回為空的方法的返回值類型怕敬。

下面這段引用時 OpenJDK 中的部分

The JSR-335 EG felt fairly strongly that Optional should not be on any more than needed to support the optional-return idiom only.
Someone suggested maybe even renaming it to "OptionalReturn".

這段話我覺得表達(dá)的就是 Opti0nal 應(yīng)該作為可能返回為空的方法的返回值類型吧。

總結(jié)

本文主要介紹了 java.util.Optional 的使用帘皿。 Opti0nal 類的目的并不是取代 null东跪,而是幫助設(shè)計出更好理解的 API 接口,通過方法返回值鹰溜,提醒開發(fā)者處理可能出現(xiàn) null 的異常情況虽填。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市曹动,隨后出現(xiàn)的幾起案子斋日,更是在濱河造成了極大的恐慌,老刑警劉巖墓陈,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恶守,死亡現(xiàn)場離奇詭異,居然都是意外死亡贡必,警方通過查閱死者的電腦和手機(jī)兔港,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赊级,“玉大人押框,你說我怎么就攤上這事±硌罚” “怎么了橡伞?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵盒揉,是天一觀的道長。 經(jīng)常有香客問我兑徘,道長刚盈,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任挂脑,我火速辦了婚禮藕漱,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崭闲。我一直安慰自己肋联,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布刁俭。 她就那樣靜靜地躺著橄仍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪牍戚。 梳的紋絲不亂的頭發(fā)上侮繁,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天,我揣著相機(jī)與錄音如孝,去河邊找鬼宪哩。 笑死,一個胖子當(dāng)著我的面吹牛第晰,可吹牛的內(nèi)容都是我干的锁孟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼但荤,長吁一口氣:“原來是場噩夢啊……” “哼罗岖!你這毒婦竟也來了涧至?” 一聲冷哼從身側(cè)響起腹躁,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎南蓬,沒想到半個月后纺非,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赘方,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年烧颖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窄陡。...
    茶點(diǎn)故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡炕淮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出跳夭,到底是詐尸還是另有隱情涂圆,我是刑警寧澤们镜,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站润歉,受9級特大地震影響模狭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜踩衩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一嚼鹉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧驱富,春花似錦锚赤、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晶疼,卻和暖如春酒贬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翠霍。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工锭吨, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寒匙。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓零如,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锄弱。 傳聞我的和親對象是個殘疾皇子考蕾,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容