Java 8新特性(三):Optional類(轉載)

原文地址

在上一篇介紹Stream流式數(shù)據(jù)處理的文章中提到了Optional類君编,這是Java 8新增的一個類,用以解決程序中常見的NullPointerException異常問題锹雏。本篇文章將詳細介紹Optional類,以及如何用它消除代碼中的null檢查寸五。

避免使用null檢查

作為Java開發(fā)人員踩叭,幾乎所有人都遇到過NullPointerException異常,大多數(shù)人遇到NullPointerException異常時都會在異常出現(xiàn)的地方加上if代碼塊來判斷值不為空公罕,比如下面的代碼:

public void bindUserToRole(User user) {
    if (user != null) {
        String roleId = user.getRoleId();
        if (roleId != null) {
            Role role = roleDao.findOne(roleId);
            if (role != null) {
                role.setUserId(user.getUserId());
                roleDao.save(role);
            }
        }
    }
}

這是比較普遍的做法器紧,為了避免出現(xiàn)NullPointerException異常,手動對可能為null值進行了處理楼眷,不過代碼看起來非常糟糕铲汪,業(yè)務邏輯被淹沒在if邏輯判斷中,也許下面的代碼看起來可讀性稍好一些:

public String bindUserToRole(User user) {
    if (user == null) {
        return;
    }

    String roleId = user.getRoleId();
    if (roleId == null) {
        return;
    }

    Role = roleDao.findOne(roleId);
    if (role != null) {
        role.setUserId(user.getUserId());
        roleDao.save(role);
    }
}

上面的代碼避免了深層的if語句嵌套摩桶,但本質上是一樣的桥状,方法內有三個不同的返回點,出錯后調試也不容易硝清,因為你不知道是那個值導致了NullPointerException異常。

基于上面的原因转晰,Java 8中引入了一個新的類Optional芦拿,用以避免使用null值引發(fā)的種種問題。

Optional類

java.util.Optional<T>類是一個封裝了Optional值的容器對象查邢,Optional值可以為null蔗崎,如果值存在,調用isPresent()方法返回true扰藕,調用get()方法可以獲取值缓苛。

創(chuàng)建Optional對象

Optional類提供類三個方法用于實例化一個Optional對象,它們分別為empty()邓深、of()未桥、ofNullable(),這三個方法都是靜態(tài)方法芥备,可以直接調用冬耿。

1、empty()方法用于創(chuàng)建一個沒有值的Optional對象:

Optional<String> emptyOpt = Optional.empty();

empty()方法創(chuàng)建的對象沒有值萌壳,如果對emptyOpt變量調用isPresent()方法會返回false亦镶,調用get()方法拋出NullPointerException異常。

2袱瓮、of()方法使用一個非空的值創(chuàng)建Optional對象:

String str = "Hello World";
Optional<String> notNullOpt = Optional.of(str);

3缤骨、ofNullable()方法接收一個可以為null的值:

Optional<String> nullableOpt = Optional.ofNullable(str);

如果str的值為null,得到的nullableOpt是一個沒有值的Optional對象尺借。

提取Optional對象中的值

如果我們要獲取User對象中的roleId屬性值绊起,常見的方式是直接獲取:

String roleId = null;
if (user != null) {
    roleId = user.getRoleId();
}

使用Optional中提供的map()方法可以以更簡單的方式實現(xiàn):

Optional<User> userOpt = Optional.ofNullable(user);
Optional<String> roleIdOpt = userOpt.map(User::getRoleId);

使用orElse()方法獲取值

Optional類還包含其他方法用于獲取值褐望,這些方法分別為:

  • orElse():如果有值就返回勒庄,否則返回一個給定的值作為默認值串前;
  • orElseGet():與orElse()方法作用類似,區(qū)別在于生成默認值的方式不同实蔽。該方法接受一個Supplier<? extends T>函數(shù)式接口參數(shù)荡碾,用于生成默認值;
  • orElseThrow():與前面介紹的get()方法類似局装,當值為null時調用這兩個方法都會拋出NullPointerException異常坛吁,區(qū)別在于該方法可以指定拋出的異常類型。

下面來看看這三個方法的具體用法:

String str = "Hello World";
Optional<String> strOpt = Optional.of(str);
String orElseResult = strOpt.orElse("Hello Shanghai");
String orElseGet = strOpt.orElseGet(() -> "Hello Shanghai");
String orElseThrow = strOpt.orElseThrow(
        () -> new IllegalArgumentException("Argument 'str' cannot be null or blank."));

此外铐尚,Optional類還提供了一個ifPresent()方法拨脉,該方法接收一個Consumer<? super T>函數(shù)式接口,一般用于將信息打印到控制臺:

Optional<String> strOpt = Optional.of("Hello World");
strOpt.ifPresent(System.out::println);

使用filter()方法過濾

Optional<String> optional = Optional.of("lw900925@163.com");
optional = optional.filter(str -> str.contains("164"));

在上面的代碼中宣增,如果filter()方法中的Lambda表達式成立玫膀,filter()方法會返回當前Optional對象值,否則爹脾,返回一個值為空的Optional對象帖旨。

如何正確使用Optional

通過上面的例子可以看出,Optional類可以優(yōu)雅的避免NullPointerException帶來的各種問題灵妨,不過解阅,你是否真正掌握了Optional的用法?假設你試圖使用Optional來避免可能出現(xiàn)的NullPointerException異常泌霍,編寫了如下代碼:

Optional<User> userOpt = Optional.ofNullable(user);
if (userOpt.isPresent()) {
    User user = userOpt.get();
    // do something...
} else {
    // do something...
}

坦白說货抄,上面的代碼與我們之前的使用if語句判斷空值沒有任何區(qū)別,沒有起到Optional的正真作用:

if (user != null) {
    // do something...
} else {
    // do something...
}

當我們從之前版本切換到Java 8的時候朱转,不應該還按照之前的思維方式處理null值蟹地,Java 8提倡函數(shù)式編程,新增的許多API都可以用函數(shù)式編程表示肋拔,Optional類也是其中之一锈津。這里有幾條關于Optional使用的建議:

  • 1、盡量避免在程序中直接調用Optional對象的get()和isPresent()方法凉蜂;
  • 2琼梆、避免使用Optional類型聲明實體類的屬性;

第一條建議中直接調用get()方法是很危險的做法窿吩,如果Optional的值為空茎杂,那么毫無疑問會拋出NullPointerException異常,而為了調用get()方法而使用isPresent()方法作為空值檢查纫雁,這種做法與傳統(tǒng)的用if語句塊做空值檢查沒有任何區(qū)別煌往。

第二條建議避免使用Optional作為實體類的屬性,它在設計的時候就沒有考慮過用來作為類的屬性,如果你查看Optional的源代碼刽脖,你會發(fā)現(xiàn)它沒有實現(xiàn)java.io.Serializable接口羞海,這在某些情況下是很重要的(比如你的項目中使用了某些序列化框架),使用了Optional作為實體類的屬性曲管,意味著他們不能被序列化却邓。

下面我們通過一些例子講解Optional的正確用法:

1、正確創(chuàng)建Optional對象‘

上面提到創(chuàng)建Optional對象有三個方法院水,empty()方法比較簡單腊徙,沒什么特別要說明的。主要是of()和ofNullable()方法檬某。當你很確定一個對象不可能為null的時候撬腾,應該使用of()方法,否則恢恼,盡可能使用ofNullable()方法民傻,比如:

public static void method(Role role) {
    // 當Optional的值通過常量獲得或者通過關鍵字new初始化,可以直接使用of()方法
    Optional<String> strOpt = Optional.of("Hello World");
    Optional<User> userOpt = Optional.of(new User());

    // 方法參數(shù)中role值不確定是否為null场斑,使用ofNullable()方法創(chuàng)建
    Optional<Role> roleOpt = Optional.ofNullable(role);
}

2饰潜、orElse()方法的使用

return str != null ? str : "Hello World"

上面的代碼表示判斷字符串str是否為空,不為空就返回和簸,否則,返回一個常量碟刺。使用Optional類可以表示為:

return strOpt.orElse("Hello World")

3锁保、簡化if-else

User user = ...
if (user != null) {
    String userName = user.getUserName();
    if (userName != null) {
        return userName.toUpperCase();
    } else {
        return null;
    }
} else {
    return null;
}

上面的代碼可以簡化成:

User user = ...
Optional<User> userOpt = Optional.ofNullable(user);

return userOpt.map(User::getUserName)
            .map(String::toUpperCase)
            .orElse(null);

總結一下,新的Optional類讓我們可以以函數(shù)式編程的方式處理null值半沽,拋棄了Java 8之前需要嵌套大量if-else代碼塊爽柒,使代碼可讀性有了很大的提高。下一篇文章將介紹Java 8中新添加的日期API者填。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末浩村,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子占哟,更是在濱河造成了極大的恐慌心墅,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件榨乎,死亡現(xiàn)場離奇詭異怎燥,居然都是意外死亡,警方通過查閱死者的電腦和手機蜜暑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門铐姚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肛捍,你說我怎么就攤上這事隐绵≈冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵依许,是天一觀的道長棺禾。 經(jīng)常有香客問我,道長悍手,這世上最難降的妖魔是什么帘睦? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮坦康,結果婚禮上竣付,老公的妹妹穿的比我還像新娘。我一直安慰自己滞欠,他們只是感情好古胆,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著筛璧,像睡著了一般逸绎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上夭谤,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天棺牧,我揣著相機與錄音,去河邊找鬼朗儒。 笑死颊乘,一個胖子當著我的面吹牛,可吹牛的內容都是我干的醉锄。 我是一名探鬼主播乏悄,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼恳不!你這毒婦竟也來了檩小?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤烟勋,失蹤者是張志新(化名)和其女友劉穎规求,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體神妹,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡颓哮,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了鸵荠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冕茅。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出姨伤,到底是詐尸還是另有隱情哨坪,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布乍楚,位于F島的核電站当编,受9級特大地震影響,放射性物質發(fā)生泄漏徒溪。R本人自食惡果不足惜忿偷,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望臊泌。 院中可真熱鬧鲤桥,春花似錦、人聲如沸渠概。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽播揪。三九已至贮喧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猪狈,已是汗流浹背箱沦。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雇庙,地道東北人饱普。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像状共,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子谁帕,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內容