java8(新特性3)---Optional

It takes a strong man to save himself, and a great man to save another.

一 概覽

 Optional是java.util包中的一部分咖祭,因此為了使用Optional么翰,需要:  import java.util.Optional

二 創(chuàng)建

2.1 調用empty API, 創(chuàng)建一個空的Optional對象:


@Test

public void whenCreatesEmptyOptional_thenCorrect() {

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

     assertFalse(empty.isPresent());

}

Ps: isPresent API 是用來檢查Optional對象中是否有值檐迟。只有當我們創(chuàng)建了一個含有非空值的Optional時才返回true追迟。在下一部分我們將介紹這個API敦间。

2.2 使用staticAPI創(chuàng)建

@Test
public void givenNonNull_whenCreatesOptional_thenCorrect() {

    String name = "baeldung";

    Optional<String> opt = Optional.of(name);    assertEquals("Optional[baeldung]", opt.toString());

}

然而,傳遞給of()的值不可以為空厢绝,否則會拋出空指針異常昔汉,如下:

@Test(expected = NullPointerException.class)
public void givenNull_whenThrowsErrorOnCreate_thenCorrect() {

   String name = null;

   Optional<String> opt = Optional.of(name);

}

有時我們需要傳遞一些空值浓利,那我們可以使用下面這個API:

@Test
public void givenNonNull_whenCreatesNullable_thenCorrect() {

    String name = "baeldung";

    Optional<String> opt = Optional.ofNullable(name);    assertEquals("Optional[baeldung]", opt.toString());

}

使用ofNullable API贷掖,則當傳遞進去一個空值時渴语,不會拋出異常,而只是返回一個空的Optional對象牙甫,如同我們用Optional.empty API:

@Test
public void givenNull_whenCreatesNullable_thenCorrect() {

    String name = null;

    Optional<String> opt = Optional.ofNullable(name);    assertEquals("Optional.empty", opt.toString());

}

三 使用isPresent API 檢查值

我們可以使用這個API檢查一個Optional對象中是否有值窟哺,只有值非空才返回true技肩。

@Test
public void givenOptional_whenIsPresentWorks_thenCorrect() {

    Optional<String> opt = Optional.of("Baeldung");

    assertTrue(opt.isPresent());

    opt = Optional.ofNullable(null);

    assertFalse(opt.isPresent());

}

四 適當情況下使用isPresent API

傳統(tǒng)上,我們一般這樣寫來檢查空值:

if(name != null){

    System.out.println(name.length);

}

問題在于旋奢,有時候我們會忘記了對空值進行檢查至朗,這時就可以使用這個API:

@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {

    Optional<String> opt = Optional.of("baeldung");

    opt.ifPresent(name -> System.out.println(name.length()));

}

五 orEse && orElseGet

5.1 orElse

這個API被用來檢索Optional對象中的值锹引,它被傳入一個“默認參數(shù)‘粤蝎。如果對象中存在一個值袋马,則返回它,否則返回傳入的“默認參數(shù)”碑宴,如下所示:

@Test
public void whenOrElseWorks_thenCorrect() {

    String nullName = null;

    String name = Optional.ofNullable(nullName).orElse("john");

    assertEquals("john", name);

}

5.2 orElseGet

與orElsel類似祸挪,但是這個函數(shù)不接收一個“默認參數(shù)”贞间,而是一個函數(shù)接口,如下例所示:

@Test
public void whenOrElseGetWorks_thenCorrect() {

    String nullName = null;

    String name = Optional.ofNullable(nullName).orElseGet(() -> "john");

    assertEquals("john", name);

}

5.3 兩者區(qū)別

要想理解這二者整以,首先讓我們創(chuàng)建一個無參且返回定值的方法:

public String getMyDefault() {

    System.out.println("Getting Default Value");

    return "Default Value";

}

接下來公黑,進行兩個測試看看它們有什么區(qū)別:

@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {

    String text;

    System.out.println("Using orElseGet:");

    String defaultText =

      Optional.ofNullable(text).orElseGet(this::getMyDefault);

    assertEquals("Default Value", defaultText);

    System.out.println("Using orElse:");

    defaultText = Optional.ofNullable(text).orElse(getMyDefault());

    assertEquals("Default Value", defaultText);

}

在這里示例中,我們的Optional對象中包含的都是一個空值吭从,讓我們看看程序執(zhí)行結果:

Using orElseGet:

Getting default value...

Using orElse:

Getting default value...

兩個Optional對象中都不存在value,因此執(zhí)行結果相同谱醇。

那么當Optional對象中值存在時又是怎樣呢枣抱?

@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {

    String text = "Text present";

    System.out.println("Using orElseGet:");

    String defaultText

      = Optional.ofNullable(text).orElseGet(this::getMyDefault);    assertEquals("Text present", defaultText);

    System.out.println("Using orElse:");

    defaultText = Optional.ofNullable(text).orElse(getMyDefault());    assertEquals("Text present", defaultText);

}

讓我們看看執(zhí)行結果:

Using orElseGet:

Using orElse:

Getting default value...

可以看到佳晶,當使用orElseGet去檢索值時讼载,getMyDefault并不執(zhí)行,因為Optional中含有值菇篡,而使用orElse時則照常執(zhí)行一喘。所以可以看到嗜暴,當值存在時议蟆,orElse相比于orElseGet咐容,多創(chuàng)建了一個對象舆逃,可能從這個實例中你感受不到影響有多大,但考慮當getDefalut不僅僅是個簡單函數(shù)戳粒,而是一個web service之類的路狮,則多創(chuàng)建一個代價是比較大的。

六 orElseThrow

orElseThrow當遇到一個不存在的值的時候蔚约,并不返回一個默認值奄妨,而是拋出異常,如下所示:

@Test(expected = IllegalArgumentException.class)
public void whenOrElseThrowWorks_thenCorrect() {

    String nullName = null;

    String name = Optional.ofNullable(nullName).orElseThrow(

      IllegalArgumentException::new);

}

七 使用get()

@Test
public void givenOptional_whenGetsValue_thenCorrect() {

    Optional<String> opt = Optional.of("baeldung");

    String name = opt.get();

    assertEquals("baeldung", name);

}

使用get() API 也可以返回被包裹著的值炊琉。但是必須是值存在時展蒂,當值不存在時又活,它會拋出一個NoSuchElementException異常苔咪,如下所示:

@Test(expected = NoSuchElementException.class)
public void givenOptionalWithNull_whenGetThrowsException_thenCorrect() {

    Optional<String> opt = Optional.ofNullable(null);

    String name = opt.get();

}

因為這個方法與我們使用Optional的目的相違背团赏,所以可以預見在不久將來它或許會被拋棄,建議還是使用其他的方法体谒。

八 filter()

接收一個函數(shù)式接口,當符合接口時故响,則返回一個Optional對象誓酒,否則返回一個空的Optional對象。

@Test
public void whenOptionalFilterWorks_thenCorrect() {

    Integer year = 2016;

    Optional<Integer> yearOptional = Optional.of(year);

    boolean is2016 = yearOptional.filter(y -> y == 2016).isPresent();

    assertTrue(is2016);

    boolean is2017 = yearOptional.filter(y -> y == 2017).isPresent();

    assertFalse(is2017);

}

這個API作用一般就是拒絕掉不符合條件的,比如拒絕掉錯誤的電子郵箱。

讓我們看下一個更有意義的例子,假如我們想買一個調制解調器扒磁,并且只關心它的價格:

public class Modem {

    private Double price;

    public Modem(Double price) {

        this.price = price;

    }

    //standard getters and setters}

接下來,我們想要檢查每一類調制解調器是否在我們可以承受的價格范圍內兰伤,那我們在不使用Optional時該如何做呢?

public boolean priceIsInRange1(Modem modem) {

    boolean isInRange = false;

    if (modem != null && modem.getPrice() != null      && (modem.getPrice() >= 10

        && modem.getPrice() <= 15)) {

        isInRange = true;

    }

    return isInRange;

}

我們竟然要寫這么多code,尤其是if條件語句,然而對于這部分code最關鍵的其實是對價格范圍的判斷。

這是一個Test例子:

@Test
public void whenFiltersWithoutOptional_thenCorrect() {

    assertTrue(priceIsInRange1(new Modem(10.0)));

    assertFalse(priceIsInRange1(new Modem(9.9)));

    assertFalse(priceIsInRange1(new Modem(null)));

    assertFalse(priceIsInRange1(new Modem(15.5)));

    assertFalse(priceIsInRange1(null));

}

如果長時間不用沃缘,那么有可能會忘記對null進行檢查,那么如果使用Optional水慨,會怎么樣呢朝抖?

public boolean priceIsInRange2(Modem modem2) {

    return Optional.ofNullable(modem2)

      .map(Modem::getPrice)

      .filter(p -> p >= 10)

      .filter(p -> p <= 15)

      .isPresent();

}

map()僅僅是將一個值轉換為另一個值砌滞,請謹記在心绊茧,這個操作并不會改變原來的值。

讓我們仔細看看這段代碼,首先况芒,當我們傳入一個null時耐版,不會發(fā)生任何問題。其次落君,我們在這段code所寫的唯一邏輯就如同此方法名所描述焙蚓。

之前的那段code為了其固有的脆弱性萌京,必須做更多,而現(xiàn)在不用了较坛。

九 map()

在之前的例子中丑勤,我們使用filter()來接受/拒絕一個一個值,而使用map()我們可以將一個值轉換為另一個值岔霸。

@Test
public void givenOptional_whenMapWorks_thenCorrect() {

    List<String> companyNames = Arrays.asList(

      "paypal", "oracle", "", "microsoft", "", "apple");

    Optional<List<String>> listOptional = Optional.of(companyNames);

    int size = listOptional

      .map(List::size)

      .orElse(0);    assertEquals(6, size);

}

在這個例子中絮爷,我們使用一個List包含了一些字符串柜蜈,然后再把這個List包裹起來藻雪,對其map()捷绒,我們這里是對這個List求它的size字逗。

map()返回的結果也被包裹在一個Optional對象中跟狱,我們必須調用合適的方法來查看其中的值。

注意到filter()僅僅是對值進行一個檢查并返回一個boolean(很奇怪扛门,照前面所述不應返回一個Optional對象嗎?)纵寝,而map()是使用現(xiàn)有的值進行計算,并且返回一個包裹著計算結果(映射結果)的Optional對象。

@Test
public void givenOptional_whenMapWorks_thenCorrect2() {

    String name = "baeldung";

    Optional<String> nameOptional = Optional.of(name);

    int len = nameOptional

    .map(String::length())

    .orElse(0);    assertEquals(8, len);

}

將filter()與map()一起使用可以做一些很強力的事情。

假設我們現(xiàn)在要檢查一個用戶的密碼,那么我們可以這樣做:

@Test
public void givenOptional_whenMapWorksWithFilter_thenCorrect() {

    String password = " password ";

    Optional<String> passOpt = Optional.of(password);

    boolean correctPassword = passOpt.filter(

      pass -> pass.equals("password")).isPresent();    assertFalse(correctPassword);

    correctPassword = passOpt

      .map(String::trim)

      .filter(pass -> pass.equals("password"))

      .isPresent();    assertTrue(correctPassword);

}

注意到佑附,如果不進行trim秃嗜,則會返回false锅锨,這里我們可以使用map()進行trim。

十 flatmap()

有時我們可以使用flatmap()替換map()谱邪,二者不同之處在于扯俱,map()只有當值不被包裹時才進行轉換晴玖,而flatmap()接受一個被包裹著的值并且在轉換之前對其解包。

我們現(xiàn)在有一個Person類:

public class Person {

    private String name;

    private int age;

    private String password;

    public Optional<String> getName() {

        return Optional.ofNullable(name);

    }

    public Optional<Integer> getAge() {

        return Optional.ofNullable(age);

    }

    public Optional<String> getPassword() {

        return Optional.ofNullable(password);

    }

    // normal constructors and setters}

我們可以像對待String一樣將其包裹起來:

Person person = new Person("john", 26);

Optional<Person> personOptional = Optional.of(person);

注意當我們包裹一個Person對象時锐帜,它將包含一個嵌套的Optional例子:

@Test
public void givenOptional_whenFlatMapWorks_thenCorrect2() {

    Person person = new Person("john", 26);

    Optional<Person> personOptional = Optional.of(person);

    Optional<Optional<String>> nameOptionalWrapper 

      = personOptional.map(Person::getName);

    Optional<String> nameOptional 

      = nameOptionalWrapper.orElseThrow(IllegalArgumentException::new);

    String name1 = nameOptional.orElse("");

    assertEquals("john", name1);

    String name = personOptional

      .flatMap(Person::getName)

      .orElse("");

    assertEquals("john", name);}

需要注意药蜻,方法getName返回的是一個Optional對象,而不是像trim那樣踱卵。這樣就生成了一個嵌套的Optional對象。

因此使用map原朝,我們還需要再解包一次锥忿,而使用flatMap()就不需要了。

錯誤不當之處請指出惶楼,謝謝右蹦。

轉載自:https://www.cnblogs.com/dzy521/p/10077132.html

?著作權歸作者所有,轉載或內容合作請聯(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
  • 那天,我揣著相機與錄音碴卧,去河邊找鬼弱卡。 笑死,一個胖子當著我的面吹牛住册,可吹牛的內容都是我干的婶博。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荧飞,長吁一口氣:“原來是場噩夢啊……” “哼凡人!你這毒婦竟也來了?” 一聲冷哼從身側響起叹阔,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤挠轴,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后条获,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忠荞,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年帅掘,在試婚紗的時候發(fā)現(xiàn)自己被綠了委煤。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡修档,死狀恐怖碧绞,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情吱窝,我是刑警寧澤讥邻,帶...
    沈念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

推薦閱讀更多精彩內容