Java8中的Optional操作
作者:湯圓
個人部落格:javalover.cc
前言
官人們好啊,我是湯圓,今天給大家帶來的是《Java8中的Optional操作》,希望有所幫助,謝謝
文章純屬原創,個人總結難免有差錯,如果有,麻煩在評論區回復或後台私信,謝啦
最近看到有幾個粉絲了(竊喜),多的話我也不說了,歡迎加入我們的榮華富貴大家庭
簡介
目的:Optional的出現主要是為了解決null指針問題,也叫NPE(NullPointerException
)
外形:Optional外形酷似容器(其實它就是一個容器),只是這個容器比較特殊,因為它只能存放一個對象,運氣不好的話這個對象還是個null
操作:Optional從操作上來看,又跟前面的Stream流式操作很像,比如過濾filter – 提取map等
下面我們用比較簡單的例子來對比著看下,Optional的一些基礎用法
先來看下目錄
目錄
-
Optional是什麼
-
沒它 VS 有它
-
核心操作
-
應用
正文
1. Optional是什麼
Optional是一個容器,只能存放一個對象(可為null)
Optional的出現是
- 一個是為了解決NPE問題(阿里開發手冊也有提到這一點,點擊可直接下載,官方鏈接)
- 另一個是為了程式碼更加清晰可讀,因為Optional這個名字的靈感就是來自英文
optional
(可選的),意思就是說這個對象可以為空,可以不為空
2. 沒它 VS 有它
下面我們用舊程式碼和新程式碼來對比著看(所謂的新舊是以Java8為分割線)
案例1:現有C類,我們要提取C.name屬性
public class OptionalDemo {
private static final String DEFAULT_NAME = "javalover";
public static void main(String[] args) {
// 傳入null,以身試法
getName(null);
}
// 取出c.name
public static void getName(C c){
// 舊程式碼 Java8之前
String name = (c!=null ? c.getName() : DEFAULT_NAME);
System.out.println("old: "+name);
// 新程式碼 Java8之後(下面的三個操作方法後面會介紹,這裡簡單了解下)
String nameNew = Optional
// 工廠方法,創建Optional<C>對象,如果c為null,則創建空的Optional<C>對象
.ofNullable(c)
// 提取name,這裡要注意,即使c==null,這裡也不會拋出NPE,而是返回空的Optional<String>,所以在處理數據時,我們不需要擔心空指針異常
.map(c1->c1.getName())
// 獲取optional的屬性值,如果為null,則返回給定的實參DEFAULT_NAME
.orElse(DEFAULT_NAME);
System.out.println("new: "+nameNew);
}
}
class C{
private String name;
public C(String name) {
this.name = name;
}
// 省略getter/setter
}
乍一看,好像Java8之前的舊程式碼更合適啊,只需要一個三目運算符
再看Optional操作,發現並沒有那麼簡潔
是這樣的,如果只是一層判斷,那普通的if判斷做起來更方便;
但是如果嵌套兩層呢,比如b.getC().getName()?
下面我們就看下,兩層嵌套會怎麼樣
例子2:現多了一個B類(依賴C類),我們要從對象B中提取C的屬性name,即b.getC().getName()
public static void getName2(B b){
// 舊程式碼
String name = (b!=null ? ( b.getC()!=null ? b.getC().getName() : DEFAULT_NAME) : DEFAULT_NAME);
// 新程式碼
String nameNew = Optional
.ofNullable(b)
.map(b1->b1.getC())
.map(c1->c1.getName())
.orElse(DEFAULT_NAME);
System.out.println(nameNew);
}
class B{
private C c;
public B(C c) {
this.c = c;
}
// 省略getter/setter
}
這次不管是乍一看,還是一直看,都是Optional更勝一籌
例子3:現多了一個A類(依賴B類),我們要提取a.getB().getC().getName()
等等等,省略號
意思到就行,反正要說的就是單從判空來看的話,Optional肯定是好過三目運算符的(if/else這裡就不舉了,它的嵌套只會更多)
3. 核心操作
因為Optional主要是操作數據(類似資料庫操作),所以我們這裡從數據的角度來進行分析
這裡我們可以分為三種操作:保存數據、處理數據、獲取數據
保存數據:
- (沒有默認值)
public static <T> Optional<T> of(T value)
:填充 T value 到 Optional 的屬性中;如果 value==null,則拋出NPE - (默認null)
public static <T> Optional<T> ofNullable(T value)
:填充 T value 到 Optional 的屬性中;如果引用為null,則填充null - (構造一個空的Optional)
public static<T> Optional<T> empty()
:單純地創建一個數據為null的空Optional,即直接填充null到 Optional 的屬性中【不常用】
處理數據:
- (提取)
public<U> Optional<U> map(Function<? super T, ? extends U> mapper)
:提取Optional中屬性T的某個屬性值U,並將U填充到新的Optional中並返回 - (過濾)
public Optional<T> filter(Predicate<? super T> predicate)
:過濾Optional中屬性T的某個屬性值,符合條件則將T填充到新的Optional中並返回 - (扁平化提取)
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper)
:提取Optional中屬性T的某個屬性Optional<U>
,直接返回
獲取數據:
public T orElse(T other)
:獲取數據,如果數據為null,則返回T otherpublic T orElseGet(Supplier<? extends T> other)
:獲取數據,如果數據為null,則通過函數式介面other返回一個新的數據Tpublic T get()
:獲取數據,如果數據為null,則報NPE【不常用】
上面這些操作中,不常用的就是get()和empty()
其他的就不舉了,這裡主要說下map()和flatMap()
如下圖所示:
map()主要是提取Optional中的屬性C的屬性name,然後再包裝到新的Optional
輸入Optional<C>
, 輸出Optional<String>
(即Optional<c.name>)
String nameNew = Optional
.ofNullable(c)
.map(c1->c1.getName())
.orElse("xxx");
flatMap()主要是提取Optional中的屬性B的Optional<C>
屬性中的C的值,然後再包裝到新的Optional
輸入Optional<B>
,輸出Optional<C>
public class FlatMapDemo {
private static final String DEFAULT_NAME = "javalover";
public static void main(String[] args) {
getName(null);
}
// 取出 b.c.name
public static void getName(B b){
C c = Optional
.ofNullable(b)
// 這裡扁平化處理,提取Optional<C>中的C
// 如果用map,則返回的是Optional<Optional<C>>
.flatMap(b->b.getC())
.orElse(new C("xxx"));
System.out.println(c.getName());
}
}
class B{
private Optional<C> c;
public Optional<C> getC() {
return c;
}
public void setC(C c) {
this.c = Optional.ofNullable(c);
}
}
class C{
private String name;
public C(String name) {
this.name = name;
}
// 省略getter/setter
}
4. 應用
從規範角度來講,是為了程式碼清晰,一看用Optional<T>
變數,就知道T可能為null;
從編碼角度來講,主要是應用在非空判斷;但是實際場景的話,有兩個
- 沒有用Optional進行包裹的參數:比如上面講到的例子,傳來的參數就是普通對象,我們就需要自己用Optional容器來包裹傳來的參數,然後進行後續操作
// 取出c.name
public static void getName(C c){
// 自己手動包裝 Optional<C>
String nameNew = Optional
.ofNullable(c)
.map(c1->c1.getName())
.orElse(DEFAULT_NAME);
System.out.println("new: "+nameNew);
}
- 有用Optional進行包裹的參數:比如資料庫查詢時,我們可以用Optional來包裹查詢的結果並返回,這樣我們分析結果的時候,只需要通過orElse()來獲取,同時還可以設定默認值
// 返回Optional<Car>,通過.orElse(defaultCar)就可以獲取返回值,如果返回值為null,還可以設定一個默認值defaultCar
Optional<Car> selectOne(SelectStatementProvider selectStatement);
總結
-
Optional是什麼:一個容器,存放一個對象,對象可以為null
-
沒它 VS 有它:看場景
-
如果只是單個的if/else判斷,那就沒它會好點;
-
如果嵌套比較多,或者本來傳來的數據就是Optional類型,那肯定是Optional合適
-
-
核心操作:不常用的這裡就不寫了
- 保存數據:工廠方法
of()
和ofNullable()
- 處理數據:map(), filter(), flatMap()
- 獲取數據:orElse()
- 保存數據:工廠方法
-
應用:主要用在非空判斷,實際場景的話,我們可以用在資料庫查詢語句中
後記
最後,感謝大家的觀看,謝謝
原創不易,期待官人們的三連喲