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; } }
控制台輸出:
返回一個空的鏈表 []
分析: 下面程式碼是上述功能實現的關鍵:
- map方法是一個映射方法,如果optionalCompany ≠ null,那麼調用此lambda方法
theCompany->theCompany.getList()
,返回含一個value==List<Employee>
的Optional對象,否則返回Optional
類的靜態對象EMPTY
; - 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…)。空間和時間上並不大的犧牲,解決了空指針異常問題,不失為一個好的程式碼編寫風格。