Java-Optional類源碼分析

  • 2020 年 2 月 18 日
  • 筆記

1.引出

我們在對象調用對象的方法、域的時候總是要進行判斷對象是否為空的操作,即空指針異常(NullPointerException)。 本質上,這是一個包含有可選值的包裝類,這意味著 Optional類既可以含有對象也可以為空。 Optional是Java8提出的新特性,就是為解決空指針異常,方便函數式編程的新特性。

1.1空指針異常所需的常見操作

我們從一個簡單的用例開始。在 Java 8 之前,任何訪問對象方法或屬性的調用都可能導致 NullPointerException

String isocode = user.getAddress().getCountry().getIsocode().toUpperCase(); 在這個小示例中,如果我們需要確保不觸發異常,就得在訪問每一個值之前對其進行明確地檢查:

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();              }          }      }  }

你看到了,這很容易就變得冗長,難以維護。


2.Optional類的源碼分析(JDK1.8)

package java.util;  //1.以下4個介面的import用於lambda表達式傳入的函數式編程,一個介面用於之後介紹的單獨一個方法的執行    import java.util.function.Consumer;  import java.util.function.Function;  import java.util.function.Predicate;  import java.util.function.Supplier;      public final class Optional<T> {    	//2.調用了4中的構造器,用於創建一個私有的value域指向null的Optional對象EMPTY(每個類只有一個且不能更改)  	//EMPTY之所以大寫是因為其作為一個引用變數是不可變的。        private static final Optional<?> EMPTY = new Optional<>();      //3.用於存儲傳入的泛型對象或基本類型,如果空,則其值為null        private final T value;        //4.其為無參構造器:用於創建一個value=null的Optional的對象,而Optional對象不等於null,且返回的不是EMPTY域,而是再創建一個結構相同的對象返回        private Optional() {          this.value = null;      }      	//5.返回一個空的Optional對象,即EMPTY,其泛型類型會被轉為調用時具體類型,儘管value指向null        public static<T> Optional<T> empty() {          @SuppressWarnings("unchecked")          Optional<T> t = (Optional<T>) EMPTY;          return t;      }    	//6.其為帶參數的構造器,返回一個指定非null值的Optional。requireNonNull方法是Objects類的泛型靜態方法(不是Object類的方法,要注意),其用途就是構造器的輸入參數value為空,則拋出空指針異常,否則,直接返回value.    	/*	相當於以下語句所達到的效果,但由於其可能會被反覆調用,:  	*	if (value == null)  	*	throw new NullPointerException();  	*	return obj;  	*  	**/        private Optional(T value) {          this.value = Objects.requireNonNull(value);      }        //7.of靜態方法是Optional的靜態工廠方法,這用於返回Optional的實例,但其輸入value必須不為null,否則拋出空指針異常。      //而Optional的構造器都是私有的,無法使用new關鍵字構造其對象。        public static <T> Optional<T> of(T value) {          return new Optional<>(value);      }  	//8.ofNUllable字面意思上就能理解就是允許輸入參數為null(Nullable)的of方法。當value==null時,調用empty()方法,  	//構造一個value值為null的Optional對象返回;否則,構造一個非空value值的optional對象返回。        public static <T> Optional<T> ofNullable(T value) {          return value == null ? empty() : of(value);      }      	//9.get方法作為一個非靜態方法,通常是被使用of/ofNullable靜態方法的Optional對象所調用,返回其所存域:value。例如下面程式碼:    	//Optional中的大多非靜態方法,都是由以下結構調用的;      	/**  object:創建的非空對象,method則是屬於object類的方法  	*	Optional.ofNullable(object).get().method();  	**/        public T get() {          if (value == null) {              throw new NoSuchElementException("No value present");          }          return value;      }    	//10.返回boolean值,用於value值是否為空的判斷結果給出,注意,這不是判斷Optional對象是否為空        public boolean isPresent() {          return value != null;      }    	//11.如果value≠null,則使用該值作為參數調用實現Consumer介面的lambda表達式;否則,不做任何事情。  	//Consumer是一個有單個泛型輸入參數、無返回值的一個由JDK提供的介面        public void ifPresent(Consumer<? super T> consumer) {          if (value != null)              consumer.accept(value);      }    	//12.其為一個非靜態方法,需要Option類的工廠方法返回對象來調用  	//首先,輸入介面類型參數(一般為lambda表達式)必須非空,否則爆空指針錯誤;  	//其次,判斷optional對象的value值是否為空,是則直接返回optional對象;  	//第三步,判斷存入的value是否匹配給定的 predicate;  	//是,則返回返回optional對象,否則返回新建的value為空的optional對象。    	//綜上,空指針異常出現在:lambda表達式為空,只有value非空且符合predicate才返回此optional對象,否則從結果上來看都是返回  	//value==null的optional對象。      public Optional<T> filter(Predicate<? super T> predicate) {          Objects.requireNonNull(predicate);          if (!isPresent())              return this;          else              return predicate.test(value) ? this : empty();      }    	//Lambda為null則拋出空指針異常。如果value==null,則返回一個value==null的Optional對象;否則,  	//對其執行調用映射函數( mapper.apply(value) )得到返回值。如果返回值不為 null,  	//則創建包含映射返回值的Optional作為map方法返回值,否則返回空Optional。  	//        public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {          Objects.requireNonNull(mapper);          if (!isPresent())              return empty();          else {              return Optional.ofNullable(mapper.apply(value));          }      }        //14.如果value ≠ null,則返回基於Optional包含的映射方法的值(mapper.apply(value)),否則(value==null)返回一個空的Optional      //當Lambda表達式為空時,以及當映射返回值為null時,拋出空指針異常。        public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {          Objects.requireNonNull(mapper);          if (!isPresent())              return empty();          else {              return Objects.requireNonNull(mapper.apply(value));          }      }       //15.如果value ≠ null,則返回value, 否則返回other。        public T orElse(T other) {          return value != null ? value : other;      }       //16.如果value ≠ null,則返回value, 否則調用實現Supplier介面的對象,調用get方法,返回方法返回值(不一定是value)。        public T orElseGet(Supplier<? extends T> other) {          return value != null ? value : other.get();      }    	//17.如果value ≠ null,則返回value,否則拋出由 Supplier 繼承的異常        public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {          if (value != null) {              return value;          } else {              throw exceptionSupplier.get();          }      }    	//18.就是簡單地對Object類的equals方法的重寫,注意,Optional對象即使value值相同,也不會返回true,而是認為是不同的對象。  	//不同於String中的equals方法。    	@Override      public boolean equals(Object obj) {          if (this == obj) {              return true;          }            if (!(obj instanceof Optional)) {              return false;          }            Optional<?> other = (Optional<?>) obj;          return Objects.equals(value, other.value);      }    	//19.調用Objects類的靜態方法hasCode,返回存在value的哈希碼,如果value==null,則返回 0。      @Override      public int hashCode() {          return Objects.hashCode(value);      }    	//20.返回一個Optional的value.tpString()方法的非空字元串,否則返回約定字元串:"Optional.empty"      @Override      public String toString() {          return value != null              ? String.format("Optional[%s]", value)              : "Optional.empty";      }  }

3.Optional類的真正作用:

Java8中推出了Optional類以及Lambda表達式,這使我們可以方便地進行程式碼的編寫。將這兩者結合,才是最常見的使用方式,也是Optional類的真正威力所在:

public class Test {      public static void main(String[] args) {          User user = new User();            Optional.of(user).ifPresent(user1 -> user1.setAge(18));          Optional.of(user).ifPresent(System.out::println);              //而不是設計為如下:          if (Optional.ofNullable(user).isPresent()) {              user.setAge(18);          }            if (Optional.ofNullable(user).isPresent()) {              System.out.println(user);          }          }  }    class User {          private int age;        public int getAge() {          return age;      }        public void setAge(int age) {          this.age = age;      }        @Override      public String toString() {          return "User{" +                  "age=" + age +                  '}';      }  }

lambda在Optional類中的應用才是學習Optional類應該掌握的方法。省略了if的以lambda輸入為參數的鏈式處理,顯得更加簡潔和擁有更強的邏輯性和更少的出錯可能。

4.Optional類案例說明:

 在我們閱讀了Optional類的源程式碼之後,一定可以對其有個初步的認識,現在我們接著閱讀關於Optional類使用的一個例子: Company類中有關於員工對象的鏈表,如以下程式碼所示:  需求:在company為空的情況下也不會拋出異常,且在不為空情況下返回一個員工鏈表的實例對象。

public class OptionalTest2 {        public static void main(String[] args) {      	/**   		* 定義員工對象,公司對象初始化。   		*/          Employee employee1 = new Employee("Fisherman");          Employee employee2 = null;          List<Employee> listOfEmployee = Arrays.asList(employee1,employee2);          Company company = new Company(null, listOfEmployee);          company =null;    		/**   		* Optional類的應用   		*/            Optional<Company> optionalCompany = Optional.ofNullable(company);          System.out.println(optionalCompany.map(theCompany->theCompany.getList()).orElseGet(()->{              System.out.println("返回一個空的鏈表");              return Collections.emptyList();          }));          }      }    /**   * 員工和公司兩個類的定義   */    class Employee {      final String name;        public Employee(String name) {          this.name = name;      }  }    class Company {      final String name;        List<Employee> list = new ArrayList<>();        public Company(String name, List<Employee> list) {          this.name = name;r          this.list = list;      }        public List<Employee> getList() {          return list;      }      }

控制台輸出:

返回一個空的鏈表  []

分析:  下面程式碼是上述功能實現的關鍵:

  1. map方法是一個映射方法,如果optionalCompany ≠ null,那麼調用此lambda方法theCompany->theCompany.getList(),返回含一個value==List<Employee>的Optional對象,否則返回Optional類的靜態對象EMPTY
  2. orElseGet方法則是判斷調用起的Optional對象若為Optional類的靜態對象EMPTY,則執行其lambda表達式,否則(返回一個空鏈表,並在控制台列印出此操作),直接返回Optional對象的value域。
        Optional<Company> optionalCompany = Optional.ofNullable(company);          System.out.println(optionalCompany.map(theCompany->theCompany.getList()).orElseGet(()->{                System.out.println("返回一個空的鏈表");              return Collections.emptyList();          }));

 我們之所以不建議返回指向null的list,這是因為由此可能會產生空指針異常,並且長度為0的空list,相關操作一般進入執行語句就相當於執行完畢推出了(長度為0,直接會退出循環、if…)。空間和時間上並不大的犧牲,解決了空指針異常問題,不失為一個好的程式碼編寫風格。