Java 8 開始新增的 Optional 類 - Optional 中的方法
fPresent() 的使用條件
ifPresent() 方法能夠讓我們在對物件進行下一步操作之前判斷我們需要操作的物件是否為 Null,在沒有 Optional 物件之前,我們通常使用下面的方法先進行判斷:
if(name != null) {
System.out.println(name.length());
}
上面的程式邏輯是,首先判斷 name 這個變數是不是為空,如果不為空的話,允許程式繼續執行下一步。
這種判斷方法不是很美觀,程式碼也比較難看,更重要的是這種判斷方法也是容易出錯的。
有誰又能夠保證我們在檢查空,並且打印出變數後,這個變數不被再次使用呢,在這個變數再次使用的時候又有誰能夠保證我們不會忘記空檢查呢?
同時,有可能在程式的執行時導致空物件異常,NullPointerException。尤其是在程式因為輸入的問題導致失敗,無法啟動的情況下,通常這種情況是因為程式本身沒有被很好的設計和編碼。
Optional 能夠非常明確的處理可能為空的變數,這個是一種比較好的編碼習慣。
讓我們看看上面的程式碼在 Java 8 的環境下是如何進行實現的。
在常用的函式程式設計的情況下,我們在物件不進行空檢查後使用函式式進行程式設計:
@Test
public void givenOptional_whenIfPresentWorks_thenCorrect() {
Optional<String> opt = Optional.of("HoneyMoose");
opt.ifPresent(name -> LOG.debug("{}", name.length()));
}
在上面的示例中,我們僅僅使用了 2 行程式碼就實現了第一種方法需要使用的 5 行程式碼。
第一行程式碼使用 Optional 物件來對我們的變數進行包裝,第二行程式碼就對已經包裝好的 Optional 物件進行相應的操作。
orElse() 方法來定義預設值
orElse() 這個方法被用來獲取 Optional 例項中內部的值。
這個方法只需要 1 個引數,如果 Optional 物件中的值不為空的話,程式將會返回 Optional 物件中的值,否則將會使用 orElse 這個方法中輸入引數的值來替代輸出。
當然,你也可以在 orElse() 呼叫一個方法,至於和 orElseGet() 有什麼不同,我們將會在後面進行說明。
考察下面的程式碼:
@Test
public void whenOrElseWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("john");
assertEquals("john", name);
}
orElseGet() 方法來定義預設值
orElseGet() 和 orElse() 方法類似。
我們都知道,如果 Optional 為空的時候,如果使用 orElse() 方法,將會使用這個方法中輸入的引數來替代返回,orElseGet() 就更近一步了。
orElseGet 提供的是一個函式式的介面,你可以在 orElseGet() 中使用函式程式設計,返回的結果就是這個函式進行運算後的結果。
@Test
public void whenOrElseGetWorks_thenCorrect() {
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(() -> "john");
assertEquals("john", name);
}
orElse() 和 orElseGet() 方法的對比
和很多程式設計師一樣,如果你是開始接觸 Java 8 的話,你可能對 orElse() 和 orElseGet() 2 個方法之間的執行不同有所不瞭解,覺得這 2 個方法在功能上都是重複的。
事實上看起來就是這樣的,但是在實際上還是有一些微妙的不同的。
如果你對這些細微的不同不夠了解的話,有可能會嚴重影響你程式的執行效率。
簡單來說就是其中定義的函式是否被執行的區別,不管前面對 Optional 的判斷是否為 null, orElse() 中呼叫的方法都會被執行,orElseGet() 卻不會。
首先,讓我們在測試類中定義一個 getMyDefault() 方法,這個方法不使用任何引數,只是列印並且返回一個字串:
public String getMyDefault() {
System.out.println("Getting Default Value");
return "Default Value";
}
然後我們分別使用 orElse() 和 orElseGet() 來進行呼叫這個方法,我們來看看有什麼不同。
Optional 物件為空的情況
@Test
public void whenOrElseGetAndOrElseOverlap_thenCorrect() {
String text = null;
String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Default Value", defaultText);
defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Default Value", defaultText);
}
正如我們所看到的,因為 Optional 物件為空,我們定義的函式都被呼叫了。
程式的輸出如下,從程式的輸出可以看出來,這 2 個方法的執行是相同的。
The side effect is:
Getting default value...
Getting default value...
Optional 物件不(NOT)為空的情況
使用上面相同的程式碼,但是這次不同的是,我們定義的 Optional 物件是不為空的
@Test
public void whenOrElseGetAndOrElseDiffer_thenCorrect() {
String text = "Text present";
LOG.debug("Using orElseGet:");
String defaultText = Optional.ofNullable(text).orElseGet(this::getMyDefault);
assertEquals("Text present", defaultText);
LOG.debug("Using orElse:");
defaultText = Optional.ofNullable(text).orElse(getMyDefault());
assertEquals("Text present", defaultText);
}
如上面的程式碼所展示的,我們需要判斷的 Optional 物件已經不為空了,程式的輸出如下所示:
Using orElseGet:
Using orElse:
Getting default value...
注意到 orElseGet() 方法在我們檢查 Optional 物件不為空的時候,就不再呼叫 getMyDefault 這個方法。
然後我們再來看看 orElse() 這個方法,儘管 Optional 物件不為空,但是 orElse() 這個方法中呼叫的方法還是被執行了一次。
如果是建立物件的話,那麼我們就建立了一個從來沒有使用過的物件,在 JVM 的垃圾回收下,這個被建立的物件隨後就被回收銷燬了。
考慮一種極端的情況,如果我們定義的 getMyDefault() 方法不是建立物件並且銷燬這麼簡單,假設我們需要進行資料庫查詢,或者 HTTP 訪問等,這個將會導致程式有很大的開銷。
通常我們都知道,在資料庫查詢的時候,建立資料庫連線是很消耗資源的。
因此這就是這個 2 個方法在使用時候的區別,主要區別就在 Optional 物件不為空的情況。