您好,登錄后才能下訂單哦!
小編給大家分享一下Java中如何使用Optional,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
提到NullPointerException(簡(jiǎn)稱NPE)異常,相信每個(gè)Java開發(fā)人員都不陌生,從接觸編程的第1天起,它就和我們?nèi)缬半S形,最近處理的線上bug中,有不少都是對(duì)象沒判空導(dǎo)致的NullPointerException異常。
引起NullPointerException異常的地方有很多,比如調(diào)用String的trim()方法,比如對(duì)BigDecimal進(jìn)行計(jì)算時(shí),比如將包裝類型轉(zhuǎn)化為基本類型時(shí),這里簡(jiǎn)單回顧下。
假設(shè)有個(gè)導(dǎo)入模版定義如下:
package com.zwwhnly.springbootaction.model; import lombok.AllArgsConstructor; import lombok.Data; /** * 導(dǎo)入模版 */ @Data @AllArgsConstructor public class ImportTemplate { /** * 模版id */ private int templateId; /** * 模版名稱 */ private String templateName; /** * 模版下載url */ private String url; /** * 備注 */ private String remark; }
然后看下如下代碼:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); System.out.println(importTemplate.getUrl()); } public static ImportTemplate getImportTemplateById(int id) { return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
正常情況下,這段代碼肯定是沒有問(wèn)題的,但當(dāng)getImportTemplateById方法返回null時(shí),這段代碼就會(huì)拋出NullPointerException異常,如下所示:
public static ImportTemplate getImportTemplateById(int id) { return null; }
為了程序能正常運(yùn)行,就要判斷importTemplate是否為null,所以代碼就修改為了:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); if (importTemplate != null) { System.out.println(importTemplate.getUrl()); } }
項(xiàng)目中類似的判空代碼應(yīng)該有很多,大家可以自行看下自己項(xiàng)目的代碼。
為了避免NullPointerException異常,JDK1.8新增了Optional類來(lái)處理空指針異常,該類位于java.util包下,提供了一系列方法,
并且可以配合Lambda表達(dá)式一起使用,使代碼看起來(lái)更加清晰,接下來(lái)我們看下它的使用方法。
2.1 創(chuàng)建實(shí)例
創(chuàng)建Optional實(shí)例有以下3種方式,分別為:
調(diào)用empty方法
Optional<ImportTemplate> optionalImportTemplate = Optional.empty();
調(diào)用of方法
ImportTemplate importTemplate = new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); Optional<ImportTemplate> optionalImportTemplate = Optional.of(importTemplate);
調(diào)用ofNullable方法(推薦)
ImportTemplate importTemplate = new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate);
值得注意的是,當(dāng)參數(shù)為null時(shí),調(diào)用of方法會(huì)拋NullPointerException異常,但調(diào)用ofNullable方法不會(huì)(更符合使用場(chǎng)景),因此推薦使用ofNullable方法:
ImportTemplate importTemplate = null; Optional<ImportTemplate> optionalImportTemplate = Optional.of(importTemplate);
2.2 判斷是否有值
可以調(diào)用isPresent
方法來(lái)判斷對(duì)象是否有值(不為null),使用方法如下所示:
ImportTemplate importTemplate = null; Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate); System.out.println(optionalImportTemplate.isPresent());
以上代碼的輸出結(jié)果為:
ImportTemplate importTemplate = new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate); System.out.println(optionalImportTemplate.isPresent());
以上代碼的輸出結(jié)果為:
看下isPresent的源碼,邏輯非常簡(jiǎn)單,就是判斷了我們傳入的對(duì)象是否有值,即不為null:
/** * Return {@code true} if there is a value present, otherwise {@code false}. * * @return {@code true} if there is a value present, otherwise {@code false} */ public boolean isPresent() { return value != null; }
2.3 獲取值
可以調(diào)用get方法來(lái)獲取對(duì)象的有值,使用方法如下所示:
ImportTemplate importTemplate = new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate); System.out.println(optionalImportTemplate.get());
以上代碼的輸出結(jié)果為:
值得注意的是,當(dāng)我們傳入的對(duì)象為null時(shí),調(diào)用get方法會(huì)拋出java.util.NoSuchElementException
異常,而不是返回null。
ImportTemplate importTemplate = null; Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(importTemplate); System.out.println(optionalImportTemplate.get());
以上代碼的輸出結(jié)果為:
看下get
方法的源碼,就可以知道原因:
public T get() { if (value == null) { throw new NoSuchElementException("No value present"); } return value; }
2.4 先用isPresent,再用get(不推薦)
然后我們回顧下文初的代碼:
ImportTemplate importTemplate = getImportTemplateById(1); if (importTemplate != null) { System.out.println(importTemplate.getUrl()); }
可能很多同學(xué)會(huì)把代碼優(yōu)化為下面這樣的寫法:
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1)); if (optionalImportTemplate.isPresent()) { System.out.println(optionalImportTemplate.get().getUrl()); }
不推薦這么使用,因?yàn)榕袛嗟牡胤經(jīng)]減少,而且還不如原來(lái)看起來(lái)清晰。
2.5 ifPresent(推薦)
那該怎么優(yōu)化呢?答案就是使用ifPresent
方法,該方法接收一個(gè)Consumer類型的參數(shù),當(dāng)值不為null時(shí),就執(zhí)行,當(dāng)值為null時(shí),就不執(zhí)行,源碼如下所示:
public void ifPresent(Consumer<? super T> consumer) { if (value != null) consumer.accept(value); }
優(yōu)化之后的代碼如下所示:
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1)); optionalImportTemplate.ifPresent(importTemplate -> System.out.println(importTemplate.getUrl()));
當(dāng)然,也可以寫更多的邏輯:
Optional<ImportTemplate> optionalImportTemplate = Optional.ofNullable(getImportTemplateById(1)); optionalImportTemplate.ifPresent(importTemplate -> { System.out.println(importTemplate.getTemplateId()); System.out.println(importTemplate.getTemplateName()); System.out.println(importTemplate.getUrl()); System.out.println(importTemplate.getRemark()); });
2.6 自定義默認(rèn)值
Optional類提供了以下2個(gè)方法來(lái)自定義默認(rèn)值,用于當(dāng)對(duì)象為null時(shí),返回自定義的對(duì)象:
orElse
orElseGet
先來(lái)看下orElse方法的使用:
public static void main(String[] args) { ImportTemplate importTemplate = null; ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate) .orElse(getDefaultTemplate()); System.out.println(firstImportTemplate); importTemplate = new ImportTemplate(2, "銷售訂單-不定規(guī)格商品導(dǎo)入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null); ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate) .orElse(getDefaultTemplate()); System.out.println(secondImportTemplate); } public static ImportTemplate getDefaultTemplate() { System.out.println("getDefaultTemplate"); return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
輸出結(jié)果:
再來(lái)看下orElseGet方法的使用:
public static void main(String[] args) { ImportTemplate importTemplate = null; ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate) .orElseGet(() -> getDefaultTemplate()); System.out.println(firstImportTemplate); importTemplate = new ImportTemplate(2, "銷售訂單-不定規(guī)格商品導(dǎo)入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null); ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate) .orElseGet(() -> getDefaultTemplate()); System.out.println(secondImportTemplate); } public static ImportTemplate getDefaultTemplate() { System.out.println("getDefaultTemplate"); return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
輸出結(jié)果:
從輸出結(jié)果看,2個(gè)方法好像差不多,第1次調(diào)用都返回了默認(rèn)模版,第2次調(diào)用都返回了傳入的模版,但其實(shí)仔細(xì)觀察,你會(huì)發(fā)現(xiàn)當(dāng)使用
orElse方法時(shí),getDefaultTemplate方法執(zhí)行了2次,但調(diào)用orElseGet方法時(shí),getDefaultTemplate方法只執(zhí)行了2次(只在第1次傳入模版為null時(shí)執(zhí)行了)。
為什么會(huì)這樣呢?帶著這個(gè)疑問(wèn),我們看下這2個(gè)方法的源碼,其中orElse方法的源碼如下所示:
public T orElse(T other) { return value != null ? value : other; }
可以看到,參數(shù)other是個(gè)對(duì)象,這個(gè)參數(shù)肯定是要傳的,但只有value為空時(shí),才會(huì)用到(返回)這個(gè)對(duì)象。
orElseGet方法的源碼如下所示:
public T orElseGet(Supplier<? extends T> other) { return value != null ? value : other.get(); }
可以看到,參數(shù)other并不是直接傳入對(duì)象,如果value為null,才會(huì)執(zhí)行傳入的參數(shù)獲取對(duì)象,如果不為null,直接返回value。
2.7 自定義異常
Optional類提供了orElseThrow方法,用于當(dāng)傳入的對(duì)象為null時(shí),拋出自定義的異常,使用方法如下所示:
public static void main(String[] args) { ImportTemplate importTemplate = new ImportTemplate(2, "銷售訂單-不定規(guī)格商品導(dǎo)入模版", "o_w-a7109db89f8d4508b4c6202889a1a2c1", null); ImportTemplate firstImportTemplate = Optional.ofNullable(importTemplate) .orElseThrow(() -> new IndexOutOfBoundsException()); System.out.println(firstImportTemplate); importTemplate = null; ImportTemplate secondImportTemplate = Optional.ofNullable(importTemplate) .orElseThrow(() -> new IndexOutOfBoundsException()); System.out.println(secondImportTemplate); }
輸出結(jié)果:
2.8 過(guò)濾數(shù)據(jù)
Optional類提供了filter方法來(lái)過(guò)濾數(shù)據(jù),該方法接收一個(gè)Predicate參數(shù),返回匹配條件的數(shù)據(jù),如果不匹配條件,返回一個(gè)空的Optional,使用方法如下所示:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); Optional<ImportTemplate> filterById = Optional.ofNullable(importTemplate) .filter(f -> f.getTemplateId() == 1); System.out.println(filterById.isPresent()); Optional<ImportTemplate> filterByName = Optional.ofNullable(importTemplate) .filter(f -> f.getTemplateName().contains("發(fā)貨單")); System.out.println(filterByName.isPresent()); } public static ImportTemplate getImportTemplateById(int id) { return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
輸出結(jié)果:
2.9 轉(zhuǎn)換值
Optional類提供了以下2個(gè)方法來(lái)轉(zhuǎn)換值:
map
flatMap
map方法的使用方法如下所示:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); Optional<String> optionalUrl = Optional.ofNullable(importTemplate) .map(f -> "url:" + f.getUrl()); System.out.println(optionalUrl.isPresent()); System.out.println(optionalUrl.get()); } public static ImportTemplate getImportTemplateById(int id) { return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
輸出結(jié)果:
flatMap方法和map方法類似,不過(guò)它支持傳入Optional,使用方法如下所示:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); Optional<String> optionalUrl = Optional.ofNullable(importTemplate) .flatMap(f -> Optional.ofNullable(f.getUrl())); System.out.println(optionalUrl.isPresent()); System.out.println(optionalUrl.get()); } public static ImportTemplate getImportTemplateById(int id) { return new ImportTemplate(1, "銷售訂單-普通商品導(dǎo)入模版", "o_w-140e3c1f41c94f238196539558e25bf7", null); }
輸出結(jié)果:
對(duì)于程序員來(lái)說(shuō),一不注意就會(huì)出現(xiàn)NullPointerException異常,避免它的方式也很簡(jiǎn)單,比如使用前判斷不能為空:
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); if (importTemplate != null) { System.out.println(importTemplate.getUrl()); } }
比如為空時(shí),直接返回(或者返回默認(rèn)值):
public static void main(String[] args) { ImportTemplate importTemplate = getImportTemplateById(1); if (importTemplate == null) { return; } System.out.println(importTemplate.getUrl()); }
比如,使用本文中的Optional。
使用哪種方式不重要,盡可能地避免NullPointerException
異常才重要。
以上是“Java中如何使用Optional”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。