轉(zhuǎn)載自:https://www.cnblogs.com/xingzc/p/5778090.html
思考: 調(diào)用一個(gè)方法得到了返回值卻不能直接將返回值作為參數(shù)去調(diào)用別的方法欺栗。
原來解決方案: 我們首先要判斷這個(gè)返回值是否為null养篓,只有在非空的前提下才能將其作為其他方法的參數(shù)。這正是一些類似Guava的外部API試圖解決的問題。
?一些JVM編程語言比如Scala背镇、Ceylon等已經(jīng)將對(duì)在核心API中解決了這個(gè)問題殿较。
新版本的Java,比如Java 8引入了一個(gè)新的Optional類澜躺。Optional類的Javadoc描述如下:
這是一個(gè)可以為null的容器對(duì)象蝉稳。如果值存在則isPresent()方法會(huì)返回true,調(diào)用get()方法會(huì)返回該對(duì)象掘鄙。
本文會(huì)逐個(gè)探討Optional類包含的方法耘戚,并通過一兩個(gè)示例展示如何使用。
of
為非null的值創(chuàng)建一個(gè)Optional操漠。
of方法通過工廠方法創(chuàng)建Optional類收津。需要注意的是,創(chuàng)建對(duì)象時(shí)傳入的參數(shù)不能為null浊伙。如果傳入?yún)?shù)為null撞秋,則拋出NullPointerException 。
//調(diào)用工廠方法創(chuàng)建Optional實(shí)例
Optional<String> name = Optional.of("Sanaulla");
//傳入?yún)?shù)為null吧黄,拋出NullPointerException.
Optional<String> someNull = Optional.of(null);
ofNullable
為指定的值創(chuàng)建一個(gè)Optional部服,如果指定的值為null,則返回一個(gè)空的Optional拗慨。
ofNullable與of方法相似廓八,唯一的區(qū)別是可以接受參數(shù)為null的情況。示例如下:
//下面創(chuàng)建了一個(gè)不包含任何值的Optional實(shí)例
//例如赵抢,值為'null'
Optional empty = Optional.ofNullable(null);
isPresent
非常容易理解
如果值存在返回true剧蹂,否則返回false。
類似下面的代碼:
//isPresent方法用來檢查Optional實(shí)例中是否包含值
if?(name.isPresent()) {
??//在Optional實(shí)例內(nèi)調(diào)用get()返回已存在的值
??System.out.println(name.get());//輸出Sanaulla
}
get
如果Optional有值則將其返回烦却,否則拋出NoSuchElementException宠叼。
上面的示例中,get方法用來得到Optional實(shí)例中的值。下面我們看一個(gè)拋出NoSuchElementException的例子:
//執(zhí)行下面的代碼會(huì)輸出:No value present
try?{
??//在空的Optional實(shí)例上調(diào)用get()冒冬,拋出NoSuchElementException
??System.out.println(empty.get());
} catch?(NoSuchElementException ex) {
??System.out.println(ex.getMessage());
}
ifPresent
如果Optional實(shí)例有值則為其調(diào)用consumer伸蚯,否則不做處理
要理解ifPresent方法,首先需要了解Consumer類简烤。簡(jiǎn)答地說剂邮,Consumer類包含一個(gè)抽象方法。該抽象方法對(duì)傳入的值進(jìn)行處理横侦,但沒有返回值挥萌。
Java8支持不用接口直接通過lambda表達(dá)式傳入?yún)?shù)。
如果Optional實(shí)例有值枉侧,調(diào)用ifPresent()可以接受接口段或lambda表達(dá)式引瀑。類似下面的代碼:
//ifPresent方法接受lambda表達(dá)式作為參數(shù)。
//lambda表達(dá)式對(duì)Optional的值調(diào)用consumer進(jìn)行處理榨馁。
name.ifPresent((value) -> {
??System.out.println("The length of the value is: "?+ value.length());
return “adf”;//此處不可以有返回值
});
orElse
如果有值則將其返回憨栽,否則返回指定的其它值。
如果Optional實(shí)例有值則將其返回辆影,否則返回orElse方法傳入的參數(shù)徒像。示例如下:
//如果值不為null,orElse方法返回Optional實(shí)例的值蛙讥。
//如果為null锯蛀,返回傳入的消息。
//輸出:There is no value present!
System.out.println(empty.orElse("There is no value present!"));
//輸出:Sanaulla
System.out.println(name.orElse("There is some value!"));
orElseGet
orElseGet與orElse方法類似次慢,區(qū)別在于得到的默認(rèn)值旁涤。orElse方法將傳入的字符串作為默認(rèn)值,orElseGet方法可以接受Supplier接口的實(shí)現(xiàn)用來生成默認(rèn)值迫像。示例如下:
//orElseGet與orElse方法類似劈愚,區(qū)別在于orElse傳入的是默認(rèn)值,
//orElseGet可以接受一個(gè)lambda表達(dá)式生成默認(rèn)值闻妓。
//輸出:Default Value
System.out.println(empty.orElseGet(() -> "Default Value"));
//輸出:Sanaulla
System.out.println(name.orElseGet(() -> "Default Value"));
orElseThrow
如果有值則將其返回菌羽,否則拋出supplier接口創(chuàng)建的異常。
在orElseGet方法中由缆,我們傳入一個(gè)Supplier接口注祖。然而,在orElseThrow中我們可以傳入一個(gè)lambda表達(dá)式或方法均唉,如果值不存在來拋出異常是晨。示例如下:
try?{
??//orElseThrow與orElse方法類似。與返回默認(rèn)值不同舔箭,
??//orElseThrow會(huì)拋出lambda表達(dá)式或方法生成的異常
??empty.orElseThrow(ValueAbsentException::new);
} catch?(Throwable ex) {
??//輸出: No value present in the Optional instance
??System.out.println(ex.getMessage());
}
ValueAbsentException定義如下:
class?ValueAbsentException extends?Throwable {
??public?ValueAbsentException() {
????super();
??}
??public?ValueAbsentException(String msg) {
????super(msg);
??}
??@Override
??public?String getMessage() {
????return?"No value present in the Optional instance";
??}
}
map
map方法文檔說明如下:
如果有值罩缴,則對(duì)其執(zhí)行調(diào)用mapping函數(shù)得到返回值。如果返回值不為null,則創(chuàng)建包含mapping返回值的Optional作為map方法返回值箫章,否則返回空Optional烙荷。
map方法用來對(duì)Optional實(shí)例的值執(zhí)行一系列操作。通過一組實(shí)現(xiàn)了Function接口的lambda表達(dá)式傳入操作炉抒。map方法示例如下:
//map方法執(zhí)行傳入的lambda表達(dá)式參數(shù)對(duì)Optional實(shí)例的值進(jìn)行修改奢讨。
//為lambda表達(dá)式的返回值創(chuàng)建新的Optional實(shí)例作為map方法的返回值。
Optional<String> upperName = name.map((value) -> value.toUpperCase());
System.out.println(upperName.orElse("No value found"));
flatMap
如果有值焰薄,為其執(zhí)行mapping函數(shù)返回Optional類型返回值,否則返回空Optional扒袖。flatMap與map(Funtion)方法類似塞茅,區(qū)別在于flatMap中的mapper返回值必須是Optional。調(diào)用結(jié)束時(shí)季率,flatMap不會(huì)對(duì)結(jié)果用Optional封裝野瘦。
flatMap方法與map方法類似,區(qū)別在于mapping函數(shù)的返回值不同飒泻。map方法的mapping函數(shù)返回值可以是任何類型T鞭光,而flatMap方法的mapping函數(shù)必須是Optional。
參照map函數(shù)泞遗,使用flatMap重寫的示例如下:
//flatMap與map(Function)非常類似惰许,區(qū)別在于傳入方法的lambda表達(dá)式的返回類型。
//map方法中的lambda表達(dá)式返回值可以是任意類型史辙,在map函數(shù)返回之前會(huì)包裝為Optional汹买。
//但flatMap方法中的lambda表達(dá)式返回值必須是Optionl實(shí)例。
upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
System.out.println(upperName.orElse("No value found"));//輸出SANAULLA
filter
filter個(gè)方法通過傳入限定條件對(duì)Optional實(shí)例的值進(jìn)行過濾聊倔。文檔描述如下:
如果有值并且滿足斷言條件返回包含該值的Optional晦毙,否則返回空Optional。
讀到這里耙蔑,可能你已經(jīng)知道如何為filter方法傳入一段代碼见妒。是的,這里可以傳入一個(gè)lambda表達(dá)式甸陌。對(duì)于filter函數(shù)我們應(yīng)該傳入實(shí)現(xiàn)了Predicate接口的lambda表達(dá)式须揣。如果你不熟悉Predicate接口,可以參考這篇文章邀层。
現(xiàn)在我來看看filter的各種用法返敬,下面的示例介紹了滿足限定條件和不滿足兩種情況:
//filter方法檢查給定的Option值是否滿足某些條件。
//如果滿足則返回同一個(gè)Option實(shí)例寥院,否則返回空Optional劲赠。
Optional<String> longName = name.filter((value) -> value.length() > 6);
System.out.println(longName.orElse("The name is less than 6 characters"));//輸出Sanaulla
//另一個(gè)例子是Optional值不滿足filter指定的條件。
Optional<String> anotherName = Optional.of("Sana");
Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
//輸出:name長(zhǎng)度不足6字符
System.out.println(shortName.orElse("The name is less than 6 characters"));
以上,我們介紹了Optional類的各個(gè)方法凛澎。下面通過一個(gè)完整的示例對(duì)用法集中展示:
public?class?OptionalDemo {
??public?static?void?main(String[] args) {
????//創(chuàng)建Optional實(shí)例霹肝,也可以通過方法返回值得到。
????Optional<String> name = Optional.of("Sanaulla");
????//創(chuàng)建沒有值的Optional實(shí)例塑煎,例如值為'null'
????Optional empty = Optional.ofNullable(null);
????//isPresent方法用來檢查Optional實(shí)例是否有值沫换。
????if?(name.isPresent()) {
??????//調(diào)用get()返回Optional值。
??????System.out.println(name.get());
????}
????try?{
??????//在Optional實(shí)例上調(diào)用get()拋出NoSuchElementException最铁。
??????System.out.println(empty.get());
????} catch?(NoSuchElementException ex) {
??????System.out.println(ex.getMessage());
????}
????//ifPresent方法接受lambda表達(dá)式參數(shù)讯赏。
????//如果Optional值不為空,lambda表達(dá)式會(huì)處理并在其上執(zhí)行操作冷尉。
????name.ifPresent((value) -> {
??????System.out.println("The length of the value is: "?+ value.length());
????});
????//如果有值orElse方法會(huì)返回Optional實(shí)例漱挎,否則返回傳入的錯(cuò)誤信息。
????System.out.println(empty.orElse("There is no value present!"));
????System.out.println(name.orElse("There is some value!"));
????//orElseGet與orElse類似雀哨,區(qū)別在于傳入的默認(rèn)值磕谅。
????//orElseGet接受lambda表達(dá)式生成默認(rèn)值。
????System.out.println(empty.orElseGet(() -> "Default Value"));
????System.out.println(name.orElseGet(() -> "Default Value"));
????try?{
??????//orElseThrow與orElse方法類似雾棺,區(qū)別在于返回值膊夹。
??????//orElseThrow拋出由傳入的lambda表達(dá)式/方法生成異常。
??????empty.orElseThrow(ValueAbsentException::new);
????} catch?(Throwable ex) {
??????System.out.println(ex.getMessage());
????}
????//map方法通過傳入的lambda表達(dá)式修改Optonal實(shí)例默認(rèn)值捌浩。
????//lambda表達(dá)式返回值會(huì)包裝為Optional實(shí)例放刨。
????Optional<String> upperName = name.map((value) -> value.toUpperCase());
????System.out.println(upperName.orElse("No value found"));
????//flatMap與map(Funtion)非常相似,區(qū)別在于lambda表達(dá)式的返回值嘉栓。
????//map方法的lambda表達(dá)式返回值可以是任何類型宏榕,但是返回值會(huì)包裝成Optional實(shí)例。
????//但是flatMap方法的lambda返回值總是Optional類型侵佃。
????upperName = name.flatMap((value) -> Optional.of(value.toUpperCase()));
????System.out.println(upperName.orElse("No value found"));
????//filter方法檢查Optiona值是否滿足給定條件麻昼。
????//如果滿足返回Optional實(shí)例值,否則返回空Optional馋辈。
????Optional<String> longName = name.filter((value) -> value.length() > 6);
????System.out.println(longName.orElse("The name is less than 6 characters"));
????//另一個(gè)示例抚芦,Optional值不滿足給定條件。
????Optional<String> anotherName = Optional.of("Sana");
????Optional<String> shortName = anotherName.filter((value) -> value.length() > 6);
????System.out.println(shortName.orElse("The name is less than 6 characters"));
??}
}
上述代碼輸出如下:
Sanaulla
No value present
The length of the value is: 8
There is no value present!
Sanaulla
Default Value
Sanaulla
No value present in?the Optional instance
SANAULLA
SANAULLA
Sanaulla
The name is less?than 6 characters
實(shí)例展示
假如有一個(gè)Person 對(duì)象迈螟,它有一個(gè)Address 屬性叉抡,而Address屬性還嵌套一個(gè)validFrom的日期。所有的值都可能是null答毫。
好了褥民,現(xiàn)在我們來判斷一下Person 的這些屬性是否是Valid的。
privateboolean?validAddress(NullPerson person){
if(person!=null){
if(person.getAddress()!=null){
final?Instant validFrom=?person.getAddress().getValidFrom();
return?validFrom!=null&&?validFrom.isBefore(now());
}else
returnfalse;
}else
returnfalse;
}
或者也可以這么寫:
return?person!=null&&
person.getAddress()!=null&&
person.getAddress().getValidFrom()!=null&&
person.getAddress().getValidFrom().isBefore(now());
總之 都不是很好看洗搂。
但如果這些屬性都是Optional消返,那么看起來會(huì)稍微舒服一點(diǎn)载弄。
class?Person{
privatefinal?Optional?address;
public?Optional?getAddress(){
return?address;
}
//...
}
class?Address{
privatefinal?Optional?validFrom;
public?Optional?getValidFrom(){
return?validFrom;
}
//...
}
以下代碼就實(shí)現(xiàn)了一樣的判斷:
return?person.
flatMap(Person::getAddress).
flatMap(Address::getValidFrom).
filter(x->?x.before(now())).
isPresent();
使用Optional以后,NullPointerException 就從此消失了撵颊。
將一個(gè)Optional轉(zhuǎn)為L(zhǎng)ist或者Set
Optional是一個(gè)集合宇攻,雖然里面只有0或者1個(gè)元素,但它一樣是一個(gè)集合倡勇。如果要轉(zhuǎn)為L(zhǎng)ist或者Set逞刷,一般的寫法可以是:
publicstatic?List?toList(Optional?option){
return?option.
map(Collections::singletonList).
orElse(Collections.emptyList());
}
或者更傳統(tǒng)的寫法:
publicstatic?List?toList(Optional?option){
if(option.isPresent())
returnCollections.singletonList(option.get());
else
returnCollections.emptyList();
}
但是在java8里,其實(shí)只需要這么寫:
importstatic?java.util.stream.Collectors.*;
//轉(zhuǎn)為L(zhǎng)ist
List?list=?collect(opt, toList());
//轉(zhuǎn)為Set
Set??set=?collect(opt, toSet());
本文代碼內(nèi)容來自于:http://www.nurkiewicz.com/2013/08/optional-in-java-8-cheat-sheet.html