Java8之Optional類培慌,巧解NPE

NullPointerException——空指針異常是程序中常見異常之一,也是導致程序運行失敗的常見異常柑爸。以前吵护,為了防止出現(xiàn)null,我們常在代碼中使用if…else…做防御性檢查,后來Guava為了解決上述方法造成的代碼污染引入了Optional類馅而。

Java8借鑒Guava的Optional也加入了同名的Optional類祥诽,Optional類提供了很多實用的方法,借此可以避免顯示的空指針判斷瓮恭,從而避免NullPointerException雄坪。

常見方法

下面逐一講解Optional類提供的方法屯蹦。

of方法
public static <T> Optional<T> of(T value) {
    return new Optional<>(value);
}

Optional中的構造方法都是private的,外部無法使用new方式創(chuàng)建Optional對象登澜,但Optional類提供三個方法用來獲取Optional的實例對象。of方法是其中之一帖渠,用來創(chuàng)建一個包裝對象值非空的Optional實例對象谒亦,若包裝對象值為空則拋出NullPointerException異常空郊。

// user若為null則會拋出NullPointerException異常
Optional<User> optional_user = Optional.of(user);
ofNullable方法
public static <T> Optional<T> ofNullable(T value) {
    return value == null ? empty() : of(value);
}

獲取Optional實例對象的方法之一,返回一個允許包裝對象值為空的Optional實例對象狞甚。ofNullableof方法的區(qū)別就在是否允許包裝對象為空锁摔。

// 可以為空,且optional_user1 == optional_user2為true
Optional<User> optional_user1 = Optional.ofNullable(null);
Optional<User> optional_user2 = Optional.ofNullable(null);
empty方法
public static<T> Optional<T> empty() {
    @SuppressWarnings("unchecked")
    Optional<T> t = (Optional<T>) EMPTY;
    return t;
}

獲取Optional實例對象的方法之一哼审,創(chuàng)建一個包裝對象值為空的Optional實例對象谐腰。

// 返回包裝對象值為空的Optional實例對象
Optional<User> optional_user = Optional.empty();
isPresent方法
public boolean isPresent() {
    return value != null;
}

判斷包裝對象值是否為空十气。

// 這里的user不為null
Optional<User> optional_user1 = Optional.ofNullable(user);
Optional<User> optional_user2 = Optional.ofNullable(null);

// 返回true
optional_user1.isPresent();
// 返回false
optional_user2.isPresent();
ifPresent方法
public void ifPresent(Consumer<? super T> consumer) {
    if (value != null)
        consumer.accept(value);
}

判斷包裝對象值是否為空春霍,如果包裝對象的值為空則調(diào)用Consumer對象的accept()方法,不為空則不調(diào)用址儒。

Optional.ofNullable(user).ifPresent(() -> {
    // 包裝對象值為空執(zhí)行的代碼
    ……
});
get方法
public T get() {
    if (value == null) {
        throw new NoSuchElementException("No value present");
    }
    return value;
}

返回包裝對象的值嫁佳,若為空則拋出NoSuchElementException異常狰住。

orElse方法
public T orElse(T other) {
    return value != null ? value : other;
}

若包裝對象不是null則返回包裝對象的實際值法挨,若為null則返回指定值叉寂,即返回傳入的參數(shù)翁逞。

Optional.ofNullable(user).orElse(new User("zhangsan", 20));
orElseGet方法
public T orElseGet(Supplier<? extends T> other) {
    return value != null ? value : other.get();
}

作用同orElse方法,只不過orElseGet的入?yún)⑹?code>Supplier對象,當Optional包裝對象為null時返回指定值彼念,返回值由Supplierget方法產(chǎn)生。

// name是一個String對象
Optional.ofNullable(name).orElseGet(() -> "zhangsan");
orElseThrow方法
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
    if (value != null) {
        return value;
    } else {
        throw exceptionSupplier.get();
    }
}

作用同orElseGet類似逐沙,當包裝對象不為空時返回包裝對象的值,為空時返回一個Throwable異常棚赔。

filter方法
public Optional<T> filter(Predicate<? super T> predicate) {
    Objects.requireNonNull(predicate);
    if (!isPresent())
        return this;
    else
        return predicate.test(value) ? this : empty();
}

過濾Optional的包裝對象徘郭,當包裝對象滿足Predicate的條件時返回該Optional實例,否則返回包裝對象為nullOptional實例胧后。

// 過濾name為zhangsan的User抱环,當不存在時打印提示信息
Optional.ofNullable(user)
    .filter(u -> u.getName.equals("zhangsan"))
    .ifPresent(() -> System.out.println("There is no zhangsan"));
map方法
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Optional.ofNullable(mapper.apply(value));
    }
}

map方法對包裝對象進行函數(shù)運算(即調(diào)用Functionapply方法)后返回一個新的Optional實例對象,新Optional實例的包裝對象可以時任意類型(不一定與原包裝對象的類型保持一致)眶痰。

如果包裝對象為null則依然返回一個包裝對象為nullOptional實例梯啤,map可以無限級調(diào)用。

// 返回一個Optional<String>
Optional.of(user).map(u -> u.getName());
flatMap方法
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
    Objects.requireNonNull(mapper);
    if (!isPresent())
        return empty();
    else {
        return Objects.requireNonNull(mapper.apply(value));
    }
}

功能同map方法黔夭,二者的區(qū)別在mapperapply方法的返回值類型不同羽嫡,map方法中的mapper返回值可以是任意類型,而flatMap中的mapper的返回值是Optional類型杭棵,map方法會自動封裝返回一個Optional氛赐。

// 返回一個Optional<String>艰管,作用同map的例子
Optional.of(user).flatMap(u -> Optional.of(u.getName()));
解決NPE

在由Optional之前蒋川,我們會使用if…else…做防御性檢查,以防止出現(xiàn)空指針異常捺球。

public String getUserName(User user) {
  if(user == null) {
    return "unknown";
  }
  return user.getName();
}

有了Optional之后,我們可以這樣做了……

public String getUserName(User user) {
  Optional<User> optional_user = Optional.ofNullable(user);
  if(!optional_user.isPresent()) {
    return "unknown";
  }
  return user.getName();
}

啊……這……

顯然這兩種方法并無本質區(qū)別裂逐,依然都有防御性檢查泣栈,依然都不夠簡潔。只不過第二種方法利用isPresent方法替換了顯示的null判斷掺涛。

正確的姿勢疼进。

public String getUserName(User user) {
  // 善用map方法
  return Optional.ofNullable(user)
                 .map(u -> u.getName())
                 .orElse("unkonwn");
}

這個例子并不能完全展示Optional的實力,再看下面的示例颠悬。

/**
* 獲取領導姓名,不使用Optional
* */
public String getBossName(User user) {
  if(user != null) {
    Company company = user.getCompany();
    if(company != null) {
      Boss boss = company.getBoss();
      if(boss != null) {
        return boss.getName();
      }
    }
  }
        
  return "unkonwn";
}

優(yōu)雅的姿勢诞外。

/**
* 獲取領導姓名灾票,使用Optional
* */
public String getBossName(User user) {
  return Optional.ofNullable(user)
                 .map(u -> u.getCompany())
                 .map(c -> c.getBoss())
                 .map(b -> b.getName())
                 .orElse("unkonwn");
}

善用Optional會使我們的代碼無比的優(yōu)雅和簡潔。

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末既们,一起剝皮案震驚了整個濱河市正什,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斯棒,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件庭惜,死亡現(xiàn)場離奇詭異穗酥,居然都是意外死亡,警方通過查閱死者的電腦和手機砾跃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進店門蜓席,熙熙樓的掌柜王于貴愁眉苦臉地迎上來课锌,“玉大人,你說我怎么就攤上這事雏胃≈景埃” “怎么了?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵统翩,是天一觀的道長此洲。 經(jīng)常有香客問我,道長呜师,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任衷畦,我火速辦了婚禮知牌,結果婚禮上,老公的妹妹穿的比我還像新娘铛嘱。我一直安慰自己,他們只是感情好墨吓,可當我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布帖烘。 她就那樣靜靜地躺著,像睡著了一般秘症。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上役耕,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天聪廉,我揣著相機與錄音,去河邊找鬼框全。 笑死干签,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的容劳。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼摹恨,長吁一口氣:“原來是場噩夢啊……” “哼娶视!你這毒婦竟也來了?” 一聲冷哼從身側響起寝凌,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤孝赫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后青柄,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體预侯,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡萎馅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年虹蒋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片峭竣。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡晃虫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扛吞,到底是詐尸還是另有隱情盘榨,我是刑警寧澤蟆融,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站型酥,受9級特大地震影響山憨,放射性物質發(fā)生泄漏弥喉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一棚亩、第九天 我趴在偏房一處隱蔽的房頂上張望虏杰。 院中可真熱鬧,春花似錦瘸彤、人聲如沸笛钝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至潭陪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間老厌,已是汗流浹背黎炉。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留淀弹,地道東北人庆械。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像缭乘,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子策幼,可洞房花燭夜當晚...
    茶點故事閱讀 44,843評論 2 354

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