Java8 中的真的 Optional 很強(qiáng)大,你用對(duì)了嗎?
來(lái)源:oschina.net/dzone.com/articles/understanding-accepting-and-leveraging-optional-in
String?isocode?=?user.getAddress().getCountry().getIsocode().toUpperCase();
if?(user?!=?null)?{
????Address?address?=?user.getAddress();
????if?(address?!=?null)?{
????????Country?country?=?address.getCountry();
????????if?(country?!=?null)?{
????????????String?isocode?=?country.getIsocode();
????????????if?(isocode?!=?null)?{
????????????????isocode?=?isocode.toUpperCase();
????????????}
????????}
????}
}
創(chuàng)建 Optional ?實(shí)例
@Test(expected?=?NoSuchElementException.class)
public?void?whenCreateEmptyOptional_thenNull()?{
????Optional?emptyOpt?=?Optional.empty();
????emptyOpt.get();
}
@Test(expected?=?NullPointerException.class)
public?void?whenCreateOfEmptyOptional_thenNullPointerException()?{
????Optional?opt?=?Optional.of(user);
}
Optional ?opt?=?Optional.ofNullable(user);
訪問(wèn) Optional 對(duì)象的值
@Test
public?void?whenCreateOfNullableOptional_thenOk()?{
????String?name?=?"John";
????Optional?opt?=?Optional.ofNullable(name);
????assertEquals("John",?opt.get());
}
@Test
public?void?whenCheckIfPresent_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????Optional?opt?=?Optional.ofNullable(user);
????assertTrue(opt.isPresent());
????assertEquals(user.getEmail(),?opt.get().getEmail());
}
opt.ifPresent(?u?->?assertEquals(user.getEmail(),?u.getEmail()));
返回默認(rèn)值
@Test
public?void?whenEmptyValue_thenReturnDefault()?{
????User?user?=?null;
????User?user2?=?new?User("[email protected]",?"1234");
????User?result?=?Optional.ofNullable(user).orElse(user2);
????assertEquals(user2.getEmail(),?result.getEmail());
}
@Test
public?void?whenValueNotNull_thenIgnoreDefault()?{
????User?user?=?new?User("[email protected]","1234");
????User?user2?=?new?User("[email protected]",?"1234");
????User?result?=?Optional.ofNullable(user).orElse(user2);
????assertEquals("[email protected]",?result.getEmail());
}
User?result?=?Optional.ofNullable(user).orElseGet(?()?->?user2);
orElse() 和 orElseGet() 的不同之處
@Test
public?void?givenEmptyValue_whenCompare_thenOk()?{
????User?user?=?null
????logger.debug("Using?orElse");
????User?result?=?Optional.ofNullable(user).orElse(createNewUser());
????logger.debug("Using?orElseGet");
????User?result2?=?Optional.ofNullable(user).orElseGet(()?->?createNewUser());
}
private?User?createNewUser()?{
????logger.debug("Creating?New?User");
????return?new?User("[email protected]",?"1234");
}
Using?orElse
Creating?New?User
Using?orElseGet
Creating?New?User
@Testpublic?void?givenPresentValue_whenCompare_thenOk()?{????User?user?=?new?User("[email protected]",?"1234");????logger.info("Using?orElse");????User?result?=?Optional.ofNullable(user).orElse(createNewUser());????logger.info("Using?orElseGet");????User?result2?=?Optional.ofNullable(user).orElseGet(()?->?createNewUser());}
Using?orElseCreating?New?UserUsing?orElseGet
返回異常
@Test
public?void?givenPresentValue_whenCompare_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????logger.info("Using?orElse");
????User?result?=?Optional.ofNullable(user).orElse(createNewUser());
????logger.info("Using?orElseGet");
????User?result2?=?Optional.ofNullable(user).orElseGet(()?->?createNewUser());
}
轉(zhuǎn)換值
@Test
public?void?whenMap_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????String?email?=?Optional.ofNullable(user)
??????.map(u?->?u.getEmail()).orElse("[email protected]");
????assertEquals(email,?user.getEmail());
}
public?class?User?{
????private?String?position;
????public?Optional?getPosition()?{
????????return?Optional.ofNullable(position);
????}
????//...
}
@Test
public?void?whenFlatMap_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????user.setPosition("Developer");
????String?position?=?Optional.ofNullable(user)
??????.flatMap(u?->?u.getPosition()).orElse("default");
????assertEquals(position,?user.getPosition().get());
}
過(guò)濾值
@Test
public?void?whenFilter_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????Optional?result?=?Optional.ofNullable(user)
??????.filter(u?->?u.getEmail()?!=?null?&&?u.getEmail().contains("@"));
????assertTrue(result.isPresent());
}
Optional 類的鏈?zhǔn)椒椒?/h2>為了更充分的使用 Optional,你可以鏈接組合其大部分方法,因?yàn)樗鼈兌挤祷叵嗤愃频膶?duì)象。 我們使用 Optional ?重寫最早介紹的示例。 首先,重構(gòu)類,使其 getter 方法返回 Optional 引用: public?class?User?{
????private?Address?address;
????public?Optional?getAddress()?{
????????return?Optional.ofNullable(address);
????}
????//?...
}
public?class?Address?{
????private?Country?country;
????public?Optional?getCountry()?{
????????return?Optional.ofNullable(country);
????}
????//?...
}
上面的嵌套結(jié)構(gòu)可以用下面的圖來(lái)表示: 
圖片 現(xiàn)在可以刪除 null 檢查,替換為 Optional 的方法: @Test
public?void?whenChaining_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????String?result?=?Optional.ofNullable(user)
??????.flatMap(u?->?u.getAddress())
??????.flatMap(a?->?a.getCountry())
??????.map(c?->?c.getIsocode())
??????.orElse("default");
????assertEquals(result,?"default");
}
上面的代碼可以通過(guò)方法引用進(jìn)一步縮減: String?result?=?Optional.ofNullable(user)
??.flatMap(User::getAddress)
??.flatMap(Address::getCountry)
??.map(Country::getIsocode)
??.orElse("default");
結(jié)果現(xiàn)在的代碼看起來(lái)比之前采用條件分支的冗長(zhǎng)代碼簡(jiǎn)潔多了。 Java 9 增強(qiáng)
我們介紹了 Java 8 的特性,Java 9 為 Optional 類添加了三個(gè)方法:or()、ifPresentOrElse() 和 stream()。 or() 方法與 orElse() 和 orElseGet() 類似,它們都在對(duì)象為空的時(shí)候提供了替代情況。or() 的返回值是由 Supplier 參數(shù)產(chǎn)生的另一個(gè) Optional 對(duì)象。 如果對(duì)象包含值,則 Lambda 表達(dá)式不會(huì)執(zhí)行: @Test
public?void?whenEmptyOptional_thenGetValueFromOr()?{
????User?result?=?Optional.ofNullable(user)
????????.or(?()?->?Optional.of(new?User("default","1234"))).get();
????assertEquals(result.getEmail(),?"default");
}
上面的示例中,如果 user 變量是 null,它會(huì)返回一個(gè) Optional,它所包含的 User 對(duì)象,其電子郵件為 “default”。 ifPresentOrElse() 方法需要兩個(gè)參數(shù):一個(gè) Consumer 和一個(gè) Runnable。如果對(duì)象包含值,會(huì)執(zhí)行 Consumer 的動(dòng)作,否則運(yùn)行 Runnable。 如果你想在有值的時(shí)候執(zhí)行某個(gè)動(dòng)作,或者只是跟蹤是否定義了某個(gè)值,那么這個(gè)方法非常有用: Optional.ofNullable(user).ifPresentOrElse(?u?->?logger.info("User?is:"?+?u.getEmail()),
??()?->?logger.info("User?not?found"));
最后介紹的是新的 stream() 方法,它通過(guò)把實(shí)例轉(zhuǎn)換為 Stream 對(duì)象,讓你從廣大的 Stream API 中受益。如果沒(méi)有值,它會(huì)得到空的 Stream;有值的情況下,Stream 則會(huì)包含單一值。 我們來(lái)看一個(gè)把 Optional 處理成 Stream 的例子: @Test
public?void?whenGetStream_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????List?emails?=?Optional.ofNullable(user)
??????.stream()
??????.filter(u?->?u.getEmail()?!=?null?&&?u.getEmail().contains("@"))
??????.map(?u?->?u.getEmail())
??????.collect(Collectors.toList());
????assertTrue(emails.size()?==?1);
????assertEquals(emails.get(0),?user.getEmail());
}
這里對(duì) Stream 的使用帶來(lái)了其 filter()、map() 和 collect() 接口,以獲取 List。 Optional ?應(yīng)該怎樣用?
在使用 Optional 的時(shí)候需要考慮一些事情,以決定什么時(shí)候怎樣使用它。 重要的一點(diǎn)是 Optional 不是 Serializable。因此,它不應(yīng)該用作類的字段。 如果你需要序列化的對(duì)象包含 Optional 值,Jackson 庫(kù)支持把 Optional 當(dāng)作普通對(duì)象。也就是說(shuō),Jackson 會(huì)把空對(duì)象看作 null,而有值的對(duì)象則把其值看作對(duì)應(yīng)域的值。這個(gè)功能在 jackson-modules-java8 項(xiàng)目中。
它在另一種情況下也并不怎么有用,就是在將其類型用作方法或構(gòu)建方法的參數(shù)時(shí)。這樣做會(huì)讓代碼變得復(fù)雜,完全沒(méi)有必要: User?user?=?new?User("[email protected]",?"1234",?Optional.empty());
使用重載方法來(lái)處理非要的參數(shù)要容易得多。 Optional 主要用作返回類型。在獲取到這個(gè)類型的實(shí)例后,如果它有值,你可以取得這個(gè)值,否則可以進(jìn)行一些替代行為。 Optional 類有一個(gè)非常有用的用例,就是將其與流或其它返回 Optional 的方法結(jié)合,以構(gòu)建流暢的API。 我們來(lái)看一個(gè)示例,使用 Stream 返回 Optional 對(duì)象的 findFirst() 方法: @Test
public?void?whenEmptyStream_thenReturnDefaultOptional()?{
????List?users?=?new?ArrayList<>();
????User?user?=?users.stream().findFirst().orElse(new?User("default",?"1234"));
????assertEquals(user.getEmail(),?"default");
}
總結(jié)
Optional 是 Java 語(yǔ)言的有益補(bǔ)充 —— 它旨在減少代碼中的 NullPointerExceptions,雖然還不能完全消除這些異常。 它也是精心設(shè)計(jì),自然融入 Java 8 函數(shù)式支持的功能。 總的來(lái)說(shuō),這個(gè)簡(jiǎn)單而強(qiáng)大的類有助于創(chuàng)建簡(jiǎn)單、可讀性更強(qiáng)、比對(duì)應(yīng)程序錯(cuò)誤更少的程序。 逆鋒起筆是一個(gè)專注于程序員圈子的技術(shù)平臺(tái),你可以收獲最新技術(shù)動(dòng)態(tài)、最新內(nèi)測(cè)資格、BAT等大廠的經(jīng)驗(yàn)、精品學(xué)習(xí)資料、職業(yè)路線、副業(yè)思維,微信搜索逆鋒起筆關(guān)注!
18 個(gè) Java8 處理日期的新花樣,肯定沒(méi)用過(guò)!
你還在 new 對(duì)象嗎?Java8 通用 Builder 了解一下
同事寫了一個(gè) update,誤用一個(gè)雙引號(hào),生產(chǎn)數(shù)據(jù)全變 0 了!
????private?Address?address;
????public?Optional?getAddress()?{
????????return?Optional.ofNullable(address);
????}
????//?...
}
public?class?Address?{
????private?Country?country;
????public?Optional
????????return?Optional.ofNullable(country);
????}
????//?...
}
public?void?whenChaining_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????String?result?=?Optional.ofNullable(user)
??????.flatMap(u?->?u.getAddress())
??????.flatMap(a?->?a.getCountry())
??????.map(c?->?c.getIsocode())
??????.orElse("default");
????assertEquals(result,?"default");
}
??.flatMap(User::getAddress)
??.flatMap(Address::getCountry)
??.map(Country::getIsocode)
??.orElse("default");
public?void?whenEmptyOptional_thenGetValueFromOr()?{
????User?result?=?Optional.ofNullable(user)
????????.or(?()?->?Optional.of(new?User("default","1234"))).get();
????assertEquals(result.getEmail(),?"default");
}
??()?->?logger.info("User?not?found"));
public?void?whenGetStream_thenOk()?{
????User?user?=?new?User("[email protected]",?"1234");
????List
??????.stream()
??????.filter(u?->?u.getEmail()?!=?null?&&?u.getEmail().contains("@"))
??????.map(?u?->?u.getEmail())
??????.collect(Collectors.toList());
????assertTrue(emails.size()?==?1);
????assertEquals(emails.get(0),?user.getEmail());
}
public?void?whenEmptyStream_thenReturnDefaultOptional()?{
????List
????User?user?=?users.stream().findFirst().orElse(new?User("default",?"1234"));
????assertEquals(user.getEmail(),?"default");
}
逆鋒起筆是一個(gè)專注于程序員圈子的技術(shù)平臺(tái),你可以收獲最新技術(shù)動(dòng)態(tài)、最新內(nèi)測(cè)資格、BAT等大廠的經(jīng)驗(yàn)、精品學(xué)習(xí)資料、職業(yè)路線、副業(yè)思維,微信搜索逆鋒起筆關(guān)注!
評(píng)論
圖片
表情
