java8的Optional
前言
java中的null是每位java開發者無法迴避的問題,也是無比痛恨的問題。邏輯上明明正常的程式,在執行的時候卻偏偏會丟擲NullPointException。在做所有的操作之前(set/get/equals等),都必須加上一行"if(xxx != null)"來進行判斷,否則有很大概率會在執行時看到一堆NullPointException。另外有些工作經驗的程式設計師都知道,就算本人心裡很放心物件不會為null,也必須判斷,否則程式碼上線前的sonar檢查一般都是通不過的。
Optional的使用
java的建立者在後來也發現了這個問題,對於null,建立者們現在的普遍觀點也是認為這是一個失誤。所以在java8中對此做出了相應的補救措施,這個補救就是Optional類。
通過將物件(空物件/非空物件)放入Optional類中,然後通過Optional提供的API來獲取和操作該類,幫助優雅的對null進行相應的處理。而不是在程式碼中寫一大堆類似"if(xxx != null)"的程式碼。
我們應該將需要操作的物件先放入Optional物件中,再進行相應的操作,這就要求我們首先得構建出Optional物件。檢視Optional的原始碼可以發現,該類的所有構造方法均被private修飾,所以需要通過Optional類提供的一些靜態方法來構建。常用的靜態方法有如下三個:
Optional optional = Optional.of(object); //如果object==null,會丟擲NullPointerException Optional optional = Optional.ofNullable(object); // 如果object==null,底層呼叫Optional.Empty()方法建立Optional物件 Optional optional = Optional.empty(); // 返回一個value==null的Optional物件
通過上述三個方法中的任意一個,都可以得到一個Optional物件,其中常用的是前兩個。
對於Optional的物件,一般還有兩個常用的API,分別是:
boolean isNull = optional.isPresent(); //判斷optional中的物件是否為非null物件 Object object = optional.get(); // 從optional中取出放置的物件。如果放置的值為null,那麼會丟擲NoSuchElementException異常
下面開始舉一個實際的小案例來說明應該如何使用Optional。假設我們有一個student的物件,現在需要呼叫其getName()方法
Student student = ....; // 通過某種方式(new/db/method等)獲得,所以可能為null // 不使用Optional的方式 if(student != null) { student.getName(); } // 使用Optional的方式 Optional<Student> optional = Optional.ofNullable(student); // 不能使用Optional.of(),因為不確實student是否為null if(optional.isPresent()) { Student stu = optional.get(); // optional.isPresent()為true,說明其中存放的物件不會為null stu.getName(); }
上述程式碼完美的實現了功能,避免的NullPotionException。但是明顯能看到,對於null的判斷反而複雜化了,而且從程式碼的閱讀角度來說,這樣的程式碼多了之後,明顯對程式碼的閱讀也會帶來不小的負擔。畢竟java中"get()"這樣的方法挺多的(像ThreadLocal等)。
所以Optional的使用的正確開啟方式明顯不是這樣的。對比java8中另一大特性Stream來看,我們會發現,java8中特別推崇的一種編碼風格就是流式程式設計。Optional不外如是。
下面我們嘗試通過流失程式設計的方式,美化下上述程式碼。
這裡引入一個新的API——isElse(object)。一般搭配Optional.ofNullable(object)使用。如果Optional.ofNullable(object)中的object==null,那麼就會走isElse作為保底,保證Optional物件中存放的物件一定不為null。所以就可以寫出如下程式碼:
// 流式程式設計。 orElse返回值是T(Optional<T>) Optional.ofNullable(student).orElse(new Student()).getName();
orElse系列方法其實有三個,一般都是配合Optional.ofNullable(object)使用,分別如下:
orElse(T other); // 保底,如果Optional.ofNullable(object)中的object==null,那就返回傳入的other orElseGet(Supplier<? extends T> other); // 保底,如果如果Optional.ofNullable(object)中的object==null,返回傳入lambda表示式返回的值 orElseThrow(Supplier<? extends X> exceptionSupplier); // 保底,如果Optional.ofNullable(object)中的object==null,返回傳入lambda表示式丟擲的異常
更多的時候,Optional配合Stream可以寫出非常簡潔的流式程式設計的程式碼,程式碼如下:
public class Test { public static void main(String[] args) { Student student = new Student(); List<String> collect = Optional.ofNullable(student.getCourses()) .orElse(new ArrayList<>()) .stream().map(s -> s) .collect(Collectors.toList()); } @Data static class Student { List<String> courses ; } }