一起來學Java8(六)——Optional

Optional類是Java8新增的一個類,其作用可以有效避免空指針異常。

Optional類的代碼比較簡單,很容易就能看懂,其實質(zhì)就是一個輔助類,把需要處理的對象做了一層包裝,然后再使用Optional中的方法時,可以有效得判斷處理對象是否為空,從而做出正確判斷。

接下來我們看下如何使用Optional。

創(chuàng)建Optional

創(chuàng)建Optional有3種方式:

  • Optional.empty() 返回一個空的Optional
  • Optional.of(不為null的對象)
  • Optional.ofNullable(可以為null的對象)

如果能夠確保入?yún)⒁欢ú粸閚ull,可以用Optional.of,因為在Optional.of內(nèi)部會判斷是否為null,如果是null則拋出異常。

如果不太確定入?yún)⑹欠駷閚ull,可以用Optional.ofNullable。

對象創(chuàng)建好了,接下來看看如何使用。

isPresent和ifPresent

isPresent()方法判斷處理對象是否為null,不為null返回true,源碼如下:

public boolean isPresent() {
    return value != null;
}

ifPresent方法有一個入?yún)?code>ifPresent(Consumer<? super T> consumer),它的意思是如果對象不為null,則運行consumer進行處理,有點類似回調(diào)函數(shù)。

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方法有個參數(shù):Predicate,這是一個函數(shù)式接口,因此我們可以使用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>對象,這個時候就應(yīng)該用到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() 會把返回的結(jié)果再次放到一個Optional中
  • optional.flatMap() 不會把結(jié)果放入放到Optional中,把這個操作交給開發(fā)者來處理,讓開發(fā)者自己返回一個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,則執(zhí)行指定的Supplier接口,由于Supplier是個函數(shù)式接口,因此可以使用Lambda表達式代替。

由此看來,方式2和方式3的處理是比較優(yōu)雅的。

方式2和方式3的區(qū)別在于,方式3可以延遲返回,只有值為null的情況下才會觸發(fā)() -> "默認值",從而避免生成無用對象,方式2不管如何都生成了"默認值"這個字符串對象。下面的例子可以說明:

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElse(getDefault()));

打?。?/p>

生成了字符串對象
1

即使Optional中的值不為null,但還是執(zhí)行了getDefault(),這完全沒必要,再來看下使用orElseGet

String s = "1";
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseGet(() -> getDefault()));

打?。?

接著再看下orElseThrow,如果值為null,則直接拋出異常

String s = null;
Optional<String> optional = Optional.ofNullable(s);
System.out.println(optional.orElseThrow(() -> new NullPointerException("不能為空")));

Optional實戰(zhàn)

{
    "user": {
        "age": 20
        ,"name": "Jim"
        ,"address": {
            "province": "浙江省"
            ,"postcode": "111111"
        }
   }
}

假設(shè)有這樣一個json字符串,現(xiàn)在要獲取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,由開發(fā)者手動返回一個Optional對象,如果使用map的話則返回Optional<Optional<JSONObject>>。

最后一句.orElse("unknown")表示如果一路走下來沒有找到值,則返回一個默認值。

Optional的優(yōu)勢是處理嵌套數(shù)據(jù)結(jié)構(gòu),如這里的json數(shù)據(jù)。假如這段json數(shù)據(jù)結(jié)構(gòu)不是完整的,postcode字段不存在,或者整個address字段都不存在,在無法保證嵌套數(shù)據(jù)中的值是否存在的情況下,使用Optional是個不錯的選擇。它都能確保有個正確的返回值。

小節(jié)

本篇主要介紹了Optional類的用法,同時演示了如何使用Optional處理嵌套數(shù)據(jù)。

定期分享技術(shù)干貨,一起學習,一起進步!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容