Java8 Optional用法和最佳實踐

根據Oracle文檔,Optional是一個容器對象,能夠包含也能夠不包含非null值。Optional在Java 8中引入,目的是解決  NullPointerExceptions的問題。本質上,Optional是一個包裝器類,其中包含對其餘對象的引用。在這種狀況下,對象只是指向內存位置的指針,而且也能夠指向任何內容。從其它角度看,Optional提供一種類型級解決方案來表示可選值而不是空引用。java

Optional以前程序員

在Java 8以前,程序員將返回null而不是Optional。這種方法有一些缺點。一種是沒有明確的方法來表示null多是一個特殊值。相比之下,在API中返回Optional是明確的聲明,其中可能沒有值。若是咱們要確保不會出現空指針異常,則須要對每一個引用進行顯式的空檢查,以下所示,咱們都贊成這是不少樣板。數據庫

// Life before Optional
    private void getIsoCode( User user){
        if (user != null) {
            Address address = user.getAddress();
            if (address != null) {
                Country country = address.getCountry();
                if (country != null) {
                    String isocode = country.getIsocode();
                    if (isocode != null) {
                        isocode = isocode.toUpperCase();
                    }
                }
            }
        }
    }

複製代碼

爲了簡化此過程,讓咱們看一下如何使用Optional類,從建立和驗證明例到使用它提供的不一樣方法並將其與返回相同類型的其餘方法組合在一塊兒,後者纔是Optional的厲害之處。編程

Optional****的特性bash

Optional類提供了大約10種方法,咱們可使用它們來建立和使用Optional類,下面將介紹如何使用它們。編程語言

建立一個Optional函數

這是用於建立可選實例的三種建立方法。測試

1. static  [Optional] [empty]()ui

返回一個空的Optional實例。this

// Creating an empty optional
Optional<String> empty = Optional.empty();
複製代碼

在返回一個空的{Optional}實例時,Optional的值不存在。不過,這樣作可能頗有誘惑力,若是對象爲空,請避免與Option.empty()返回的實例的{==}比較  。由於不能保證它是一個單例,反之,應該使用isPresent()。

2. static  [Optional] [of](T value)

返回特定的非空值Optional。

// Creating an optional using of

String name = "java";

Optional<String> opt = Optional.of(name);
複製代碼

靜態方法須要一個非null參數;不然,將引起空指針異常。所以,若是咱們不知道參數是否爲null,那就是咱們使用  ofNullable的時候,下面將對此進行介紹。

3. static  [Optional] [of](T value)

返回描述指定值的Optional,若是非空,則返回空值。

// Possible null value

 Optional<String> optional = Optional.ofNullable(name());

  private  String  name(){

  String name = "Java";

  return (name.length() > 5) ? name : null;

 }
複製代碼

若是咱們傳入一個空引用,它不會拋出異常,而是返回一個空的Optional對象:

因此這就是動態或手動建立Optional的三種方法。下一組方法用於檢查值的存在。

1. 布爾值**[isPresent]****()**

若是存在值,則返回true;反之,返回false。若是所包含的對象不爲null,則返回true,反之返回false。一般在對對象執行任何其餘操做以前,先在Optional上調用此方法。 ··· //ispresent

Optional optional1 = Optional.of("javaone");

if (optional1.isPresent()){

//Do something, normally a get

} ··· 2. 布爾值[isEmpty()]

若是存在值,則返回false;不然,返回ture。這與isPresent 相反,  而且僅在Java 11及更高版本中可用。

//isempty

Optional<String> optional1 = Optional.of("javaone");

if (optional1.isEmpty()){

  //Do something

}
複製代碼

3. void [ifPresent]([Consumer]<? super [T]> consumer)

若是存在值,則使用該值調用指定的使用者;不然,什麼都不作。

若是您不熟悉Java 8,那麼您可能會想知道:什麼是消費者?簡單來講,消費者是一種接受參數且不返回任何內容的方法。當使用  ifPresent時,這個方法就是一石二鳥。咱們能夠執行值存在性檢查並使用一種方法執行預期的操做,以下所示。

//ifpresent

Optional<String> optional1 = Optional.of("javaone");

optional1.ifPresent(s -> System.out.println(s.length()));
複製代碼

可選類提供了另外一組用於獲取可選值的方法。

1. T[get]()

若是此Optional中存在值,則返回該值,不然拋出  NoSuchElementException。在這以後,咱們想要的是存儲在Optional中的值,咱們能夠經過get()來獲取它。可是,當該值爲null時,此方法將引起異常。這就須要  orElse() 方法來緊急救援。

//get
Optional<String> optional1 = Optional.of("javaone");
if (optional1.isPresent()){ 
  String value = optional1.get();
}
複製代碼

2. [][orElse][T]其餘)

返回值(若是存在);反之,返回其餘。

該  orElse() 方法用於檢索包裝在Optional實例內的值。它採用一個充當默認值的參數。該  orElse() 方法返回包裝的值(若是存在)及其參數,反之:

//orElse
        String nullName = null;
        String name = Optional.ofNullable(nullName).orElse("default_name");

複製代碼

若是這還不夠,那麼Optional類將繼續提供另外一種獲取值的方法,即便該方法的null稱爲 orElseGet()。

3. [T][orElseGet]([Supplier]<? extends [T]> other)

返回值(若是存在);不然,調用other並返回該調用的結果。

該orElseGet() 方法相似於 orElse()。可是,若是沒有Optional值,則不採用返回值,而是採用供應商功能接口,該接口將被調用並返回調用的值:

//orElseGet
        String name = Optional.ofNullable(nullName).orElseGet(() -> "john");

複製代碼

那麼,orElse() 和orElseGet()之間有什麼區別。

乍一看,這兩種方法彷佛具備相同的效果。可是,事實並不是如此。讓咱們建立一些示例,以突出二者之間的類似性和行爲差別。

首先,讓咱們看看它們在對象爲空時的行爲:

String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getDefaultValue);
defaultText = Optional.ofNullable(text).orElse(getDefaultValue());
public String getDefaultValue() {
    System.out.println("Getting Default Value");
    return "Default Value";
}

複製代碼

在上面的示例中,咱們在Optional對象中包裝了一個空文本,而後嘗試使用兩種方法中的每一種來獲取包裝後的值。反作用以下:

Getting default value...
Getting default value...
複製代碼

在每種狀況下都會調用默認方法。碰巧的是,當不存在包裝的值時,二者  orElse() 和的  orElseGet() 工做方式徹底相同。

如今,讓咱們運行另外一個該值存在測試,理想狀況下,甚至不該建立默認值:

在這個簡單的示例中,建立默認對象不會花費不少成本,由於JVM知道如何處理此類對象。可是,當諸如此類的方法  default 必須進行Web服務調用或者查詢數據庫時,則成本變得很是明顯。

使用Optional最佳實踐

就像編程語言的任何其餘功能同樣,它能夠正確使用或被濫用。爲了瞭解使用Optional類的最佳方法,須要瞭解如下內容:

**1.**它解決的問題

Optional的方法是嘗試經過增長構建更具表現力的API的可能性來減小Java系統中空指針異常的狀況,這些API解釋了有時缺乏返回值的可能性。

若是從一開始就存在Optional,那麼大多數庫和應用程序可能會更好地處理缺乏的返回值,從而減小了空指針異常的數量以及整體上的錯誤總數。

**2.**它不解決的問題

Optional並不意味着是一種避免全部類型的空指針的機制。例如,它仍然必須測試方法和構造函數的強制輸入參數。

像使用null時同樣,Optional不能幫助傳達缺失值的含義。以相似的方式,null可能意味着不少不一樣的東西(找不到值等),所以缺乏Optional值也能夠。

該方法的調用方仍然須要檢查該方法的JavaDoc以理解缺省選項的含義,以便正確地處理它。

一樣,以一種相似的方式,能夠將檢查的異常捕獲在一個空塊中,沒有什麼阻止調用方進行調用  get() 並繼續進行。

**3.**什麼時候使用

Optional的預期用途主要是做爲返回類型。獲取此類型的實例後,能夠提取該值(若是存在)或提供其餘行爲(若是不存在)。

Optional類的一個很是有用的用例是將其與流或返回Optional值以構建流暢的API的其餘方法結合。請參見下面的代碼段

User user = users.stream().findFirst().orElse(new User("default", "1234"));
複製代碼

**4.**何時不使用

a)不要將其用做類中的字段,由於它不可序列化

若是確實須要序列化包含Optional值的對象,則Jackson庫提供了將Optionals視爲普通對象的支持。這意味着Jackson將空對象視爲空,將具備值的對象視爲包含該值的字段。能夠在jackson-modules-java8項目中找到此功能。

b)不要將其用做構造函數和方法的參數,由於這會致使沒必要要的複雜代碼。

User user = new User("john@gmail.com", "1234", Optional.empty());
複製代碼

本人創業團隊產品MadPecker,主要作BUG管理、測試管理、應用分發 網址:[www.madpecker.com],有須要的朋友歡迎試用、體驗! 本文爲MadPecker團隊技術人員譯製,轉載請標明出處

相關文章
相關標籤/搜索