Optional類是Java8新增的一個類,其作用可以有效避免空指針異常。
Optional類的代碼比較簡單,很容易就能看懂,其實質就是一個輔助類,把需要處理的對象做了一層包裝,然後再使用Optional中的方法時,可以有效得判斷處理對象是否爲空,從而做出正確判斷。
接下來我們看下如何使用Optional。
創建Optional
創建Optional有3種方式:
- Optional.empty() 返回一個空的Optional
- Optional.of(不爲null的對象)
- Optional.ofNullable(可以爲null的對象)
如果能夠確保入參一定不爲null,可以用Optional.of
,因爲在Optional.of
內部會判斷是否爲null,如果是null則拋出異常。
如果不太確定入參是否爲null,可以用Optional.ofNullable
。
對象創建好了,接下來看看如何使用。
isPresent和ifPresent
isPresent()方法判斷處理對象是否爲null,不爲null返回true,源碼如下:
public boolean isPresent() {
return value != null;
}
ifPresent方法有一個入參ifPresent(Consumer<? super T> consumer)
,它的意思是如果對象不爲null,則運行consumer進行處理,有點類似回調函數。
String s = "hello";
Optional<String> optional = Optional.of(s);
if(optional.isPresent()) {
System.out.println("the value is " + optional.get());
}
同樣可以寫成:
optional.ifPresent((val) -> {
System.out.println("the value is " + val);
});
filter
filter是對處理對象進行判斷,如果判斷爲true,則返回當前Optional,如果爲false則返回一個空的Optional對象,其源碼如下:
public Optional<T> filter(Predicate<? super T> predicate) {
Objects.requireNonNull(predicate);
if (!isPresent())
return this;
else
return predicate.test(value) ? this : empty();
}
filter方法有個參數:Predicate,這是一個函數式接口,因此我們可以使用Lambda表達式來處理。
String s = "hello";
Optional<String> optional = Optional.of(s);
boolean exist = optional
.filter(val -> "hello1".equals(val))
.isPresent();
System.out.println(exist); // false
map和flatMap
map方法的作用可以簡單理解成從處理對象中取出其它對象,然後返回一個新的Optional。如下代碼所示:
public class OptionalMapTest {
static class Goods {
private String goodsName;
private Company company;
...getter setter
}
static class Company {
private String companyName;
...getter setter
}
public static void main(String[] args) {
Company company = new Company();
company.setCompanyName("Apple");
Goods goods = new Goods();
goods.setGoodsName("iphoneX");
goods.setCompany(company);
Optional<Goods> optional = Optional.of(goods);
String companyName = optional
// 從goods中取出Company,返回一個新的Optional<Company>
.map(goodsObj -> goodsObj.getCompany())
// 從company中取出companyName,返回一個新的Optional<String>
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
System.out.println(companyName);
}
}
什麼情況下該使用flatMap呢,我們把Goods中的的Company對象改成Optional<Company>
。
static class Goods {
private String goodsName;
private Optional<Company> company;
...getter setter
}
此時下面這段代碼會編譯報錯
String companyName = optional
// 從goods中取出Company,返回一個新的Optional<Company>
.map(goodsObj -> goodsObj.getCompany()) // !!這裏會報錯
// 從company中取出companyName,返回一個新的Optional<String>
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
主要是這行代碼optional.map(goodsObj -> goodsObj.getCompany())
。因爲此時返回的是一個Optional<Optional<Company>>
對象。
而我們需要的是Optional<Company>
對象,這個時候就應該用到flatMap了,只要把optional.map(goodsObj -> goodsObj.getCompany())
改成optional.flatMap(goodsObj -> goodsObj.getCompany())
即可。
String companyName = optional
// 從goods中取出Company,返回一個新的Optional<Company>
.flatMap(goodsObj -> goodsObj.getCompany())
// 從company中取出companyName,返回一個新的Optional<String>
.map(companyObj -> companyObj.getCompanyName())
// 得到companyName
.get();
簡單的理解就是:
- optional.map() 會把返回的結果再次放到一個Optional中
- optional.flatMap() 不會把結果放入放到Optional中,把這個操作交給開發者來處理,讓開發者自己返回一個Optional
get,orElse,orElseGet,orElseThrow
- get():返回被處理的值,如果值爲空,則拋出異常
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.get()); // 拋出java.util.NoSuchElementException: No value present
針對這種情況,有幾種處理方式
方式1:使用isPresent()
String s = null;
Optional<String> optional = Optional.ofNullable(s);
if (optional.isPresent()) {
System.out.println(optional.get());
} else {
System.out.println("默認值");
}
方式2:使用orElse(默認值)
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse("默認值"));
orElse(默認值)的意思是如果Optional中的值爲null,則返回給定的默認值。
方式3:使用orElseGet(Supplier)
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> "默認值"));
orElse(Supplier)的意思是如果Optional中的值爲null,則執行指定的Supplier接口,由於Supplier是個函數式接口,因此可以使用Lambda表達式代替。
由此看來,方式2和方式3的處理是比較優雅的。
方式2和方式3的區別在於,方式3可以延遲返回,只有值爲null的情況下才會觸發() -> "默認值"
,從而避免生成無用對象,方式2不管如何都生成了"默認值"這個字符串對象。下面的例子可以說明:
String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse(getDefault()));
private String getDefault() {
System.out.println("生成了字符串對象");
return "1";
}
打印:
生成了字符串對象
1
即使Optional中的值不爲null,但還是執行了getDefault(),這完全沒必要,再來看下使用orElseGet
String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> getDefault()));
private String getDefault() {
System.out.println("生成了字符串對象");
return "1";
}
打印:1
接着再看下orElseThrow,如果值爲null,則直接拋出異常
String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseThrow(() -> new NullPointerException("不能爲空")));
Optional實戰
{
"user": {
"age": 20,
"name": "Jim",
"address": {
"province": "浙江省",
"postcode": "111111"
}
}
}
假設有這樣一個json字符串,現在要獲取postcode信息。如果不用Optional的話,要寫各種if…else語句,還要判斷字段是否存在。
String postcode = "unknown";
JSONObject user = jsonObj.getJSONObject("user");
if (user != null) {
JSONObject address = user.getJSONObject("address");
if (address != null) {
String code = address.getString("postcode");
if (postcode != null) {
postcode = code;
}
}
}
System.out.println(postcode);
但是用Optional可以這樣寫:
JSONObject jsonObj = JSON.parseObject(json);
String postcode = Optional.ofNullable(jsonObj)
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("user")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getJSONObject("address")))
.flatMap(jsonObject -> Optional.ofNullable(jsonObject.getString("postcode")))
.orElse("unknown");
System.out.println(postcode);
注意,這裏要使用flatMap,由開發者手動返回一個Optional對象,如果使用map的話則返回Optional<Optional<JSONObject>>
。
最後一句.orElse(“unknown”)表示如果一路走下來沒有找到值,則返回一個默認值。
Optional的優勢是處理嵌套數據結構,如這裏的json數據。假如這段json數據結構不是完整的,postcode字段不存在,或者整個address字段都不存在,在無法保證嵌套數據中的值是否存在的情況下,使用Optional是個不錯的選擇。它都能確保有個正確的返回值。
小節
本篇主要介紹了Optional類的用法,同時演示瞭如何使用Optional處理嵌套數據。
歡迎關注作者微信公衆號:猿敲月下碼,第一時間獲得技術分享