公司做網(wǎng)站費用會計分錄谷歌是如何運營的
Optional 詳解
- 1、Optional 介紹
- 2、創(chuàng)建 Optional 對象
- 3、Optional 常用方法
- 1. 判斷值是否存在 — isPresent()
- 2. 非空表達(dá)式 — ifPresent()
- 3. 設(shè)置(獲取)默認(rèn)值 — orElse()、orElseGet()
- 4. 獲取值 — get()
- 5. 過濾值 — filter()
- 6. 轉(zhuǎn)換值 — map()
作為一名 Java 程序員,我真的是煩透了 NullPointerException(NPE),盡管和它熟的像一位老朋友,知道它也是迫不得已——程序正在使用一個對象,卻發(fā)現(xiàn)這個對象的值為 null,于是 Java 虛擬機就怒發(fā)沖冠的把它拋出來當(dāng)作替罪羊。
當(dāng)然了,我們程序員是富有責(zé)任心的,不會坐視不管,于是就有了大量的 null 值檢查。盡管有時候這種檢查完全沒必要,但我們已經(jīng)習(xí)慣了例行公事。終于,Java 8 看不下去了,就引入了 Optional,以便于我們編寫的代碼不再那么刻薄呆板。
1、Optional 介紹
- Optional 類是一個可以為 null 的容器對象。如果值存在則 isPresent() 方法會返回 true,調(diào)用 get() 方法會返回該對象。
- Optional 是一個容器:它可以保存類型 T 的值,或者僅僅保存 null。Optional 提供很多有用的方法,這樣我們就不用顯示進(jìn)行空值檢測。
- Optional 類的引用很好的解決了空指針異常。
2、創(chuàng)建 Optional 對象
1)可以使用靜態(tài)方法 empty()
創(chuàng)建一個空的 Optional 對象
Optional<Object> empty = Optional.empty();
System.out.println(empty); // 輸出:Optional.empty
2)可以使用靜態(tài)方法of()
創(chuàng)建一個非空的 Optional 對象
Optional<Object> opt = Optional.of("王二");
System.out.println(opt); // 輸出:Optional[王二]
當(dāng)然了,傳遞給of()
方法的參數(shù)必須是非空的,也就是說不能為 null ,否則仍然會拋出 NullPointerException。
3)可以使用靜態(tài)方法ofNullable()
創(chuàng)建一個即可空又可非空的 Optional 對象
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 輸出:Optional.empty
ofNullable()
方法內(nèi)部有一個三元表達(dá)式,如果參數(shù)為 null,則返回私有常量 empty;否則使用 new 關(guān)鍵字創(chuàng)建一個心的 Optional 對象——不會再拋出 NPE 異常了。
3、Optional 常用方法
1. 判斷值是否存在 — isPresent()
isPresent()
方法可以判斷一個 Optional 對象是否存在,如果存在,返回 true,否則返回 false。該方法取代了 obj != null 的判斷
Optional<String> opt = Optional.of("王二");
System.out.println(opt.isPresent()); // 輸出:trueOptional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 輸出:false
Java 11 后還可以通過方法isEmpty()
(判斷值是否為空),判斷與isPresent()
相反的結(jié)果
Optional<String> opt = Optional.of("王二");
System.out.println(opt.isEmpty()); // 輸出:falseOptional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isEmpty()); // 輸出:true
2. 非空表達(dá)式 — ifPresent()
ifPresent()
是 Optional 類的一個非?,F(xiàn)代化的方法,允許我們使用函數(shù)式編程的方法執(zhí)行一些代碼。如果沒有該方法的話,我們通常需要先通過isPresent()
方法對 Optional 對象進(jìn)行判空后再執(zhí)行相應(yīng)的代碼:
Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {System.out.println(optOrNull.get().length());
}
而有了ifPresent()
之后,情況就完全不同了,可以直接講 Lambda 表達(dá)式傳遞給該方法,代碼更加簡潔、直觀。
Optional<String> opt = Optional.of("王二");
opt.ifPresent(x -> System.out.println(x.length()));
Java 9 后還可以通過方法ifPresentOrElse(action, emptyAction)
執(zhí)行兩種結(jié)果,非空時執(zhí)行 action,空時執(zhí)行 emptyAction。
Optional<String> opt = Optional.of("王二");
opt.ifPresentOrElse(x -> System.out.println(x.length()), () -> System.out.println("為空"));
3. 設(shè)置(獲取)默認(rèn)值 — orElse()、orElseGet()
有時候,我們在創(chuàng)建(獲取) Optional 對象的時候,需要一個默認(rèn)值,orElse()
和orElseGet()
方法就派上用場了。
orElse()
方法用于返回包裹在 Optional 對象中的值,如果該值不為 null ,則返回;否則返回默認(rèn)值。該方法的參數(shù)類型和值的類型一致
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("王二");
System.out.println(name); // 輸出:王二
orElseGet()
方法與orElse()
類似,但參數(shù)類型不同。如果 Optional 對象中的值為 null,則執(zhí)行參數(shù)中的函數(shù)
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 輸出:沉默王二
從輸出結(jié)果以及代碼的形式上來看,這兩個方法極其相似,這不免引起我們的懷疑,Java 類庫的設(shè)計者有必要這做嗎?
假設(shè)現(xiàn)在有這樣一個獲取默認(rèn)值的方法,很傳統(tǒng)的方式。
public static String getDefaultValue() {System.out.println("www111");return "w1";}
然后通過orElse()
與orElseGet()
方法分別調(diào)用getDefaultValue()
返回默認(rèn)值:
String nullName = null;
String orElse = Optional.ofNullable(nullName).orElse(getDefaultValue());
System.out.println("orElse: "+ orElse);
// 類名::方法名 是 Java 8 引入的語法,方法名后面沒有 () 的,表明該方法不一定會被調(diào)用
String orElseGet = Optional.ofNullable(nullName).orElseGet(QuesRecordServiceImpl::getDefaultValue);
System.out.println("orElseGet: "+ orElseGet);System.out.println("======================");String name = "w2";
String orElse2 = Optional.ofNullable(name).orElse(getDefaultValue());
System.out.println("orElse2: "+ orElse2);
String orElseGet2 = Optional.ofNullable(name).orElseGet(QuesRecordServiceImpl::getDefaultValue);
System.out.println("orElseGet2: "+ orElseGet2);
結(jié)果如下:
咦,在 Optional 對象的值不為 null 時,orElseGet()
沒有去調(diào)用getDefaultValue()
。哪個方法的性能更佳,你明白了吧?
4. 獲取值 — get()
直觀從語義上來看,get()
方法才是最正宗的獲取 Optional 對象值的方法,但很遺憾,該方法是有缺陷的,因為假如 Optional 對象的值為 null,該方法會拋出 NoSuchElementException 異常。這完全與我們使用 Optional 類的初衷相悖。
public class GetOptionalDemo {public static void main(String[] args) {String name = null;Optional<String> optOrNull = Optional.ofNullable(name);System.out.println(optOrNull.get());}
}
這段程序在運行時會拋出異常:
盡管拋出的異常是 NoSuchElementException 而不是 NEP,但在我們看來,顯然是 “五十步笑百步”。建議使用orElseGet()
方法獲取 Optional 對象的值。
5. 過濾值 — filter()
filter()
方法可以傳入一個 Lambda 表達(dá)式作為條件,如果表達(dá)式結(jié)果為 false,則返回一個 empty 的 Optional 對象,否則返回過濾后的 Optional 對象。
String password = "12345";
Optional<String> opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent()); // 輸出:false
filter()
方法的參數(shù)類型為 Predicate(Java 8 新增的一個函數(shù)式接口),在上例中,由于 password 所以結(jié)果為 false。假設(shè)密碼長度要求在 6 到 10 位之間,那么還可以再追加一個條件:
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result); // 輸出:true
這次結(jié)果為 true。因為密碼變成了 7 位,符合條件。想象一下,假如使用 if-else 來完成這個任務(wù),代碼該有多冗長。
6. 轉(zhuǎn)換值 — map()
map()
方法可以按照一定的規(guī)則將原有 Optional 對象轉(zhuǎn)換為一個新的 Optional 對象,原有的 Optional 對象不會更改。
String name = "王二";
Optional<String> nameOptional = Optional.of(name);
Optional<Integer> intOpt = nameOptional.map(String::length);System.out.println( intOpt.orElse(0)); // 輸出:2
在上面這個例子中,map()
方法的入?yún)?code>String::length,意味著要將原有字符串類型的 Optional 按照字符串長度重新生成一個新的 Optional 對象,類型為 Integer。
當(dāng)入?yún)⒁彩且粋€ Optional 時,經(jīng)過map()
轉(zhuǎn)化后會形成一個 Optional<Optional< Integer >> 這種嵌套結(jié)構(gòu);但flatMap()
可以把這種嵌套結(jié)構(gòu)打開:
Optional<Optional<Integer>> unFlatMap = nameOptional.map(x -> Optional.of(x.length()));
Optional<Integer> flatMap = nameOptional.flatMap(x -> Optional.of(x.length()));
好事定律:每件事最后都會是好事,如果不是好事,說明還沒到最后。