Modern Java Recipes 筆記 - Optional (1)

Posted by Tim Lin on 2020-03-17

來寫一篇放了好久的筆記…, Java 8 其實也問世蠻久了, 但大概是前年新專案開始後, 才開始轉換成寫 Java 8 以上的版本, 一路走來都靠 Google 其實, 在之前幾個月幾家外商的面試, 猛磕了一陣子 Java 8, 才發現真的是另一個世界呢, 好方便XD, 其中我對於 Optional 其實充滿了比較多的疑問, 所以我整本書就是先從中間的 Optional 開始看的, 畢竟 Stream API 那些的我已經比較熟了, 所以就從 Optional, 開始吧!

Optional

很多人以為用 Optional 是為了去除 NullPointerException, 我也以為是這樣

但其實 Optional 是為了表示合法的 null 回傳, 代表這次回傳的資料 “沒有剩下的元素了的意思”, 像是使用 Stream API 在使用 reduce, min, max, findFisrt, findAny 篩選元素後, 沒有資料的狀況…當然有時候也可以表示成自己想要的無資料的表示方式

Create Optional

Optional 是不可變 (immutable) 的

但物件存在 Optional 裡面的話, 要看物件本身是不是可變或不可變

可變的物件: 幾乎所有物件都是
不可變的“物件”像是: String, 和其他 8 個基本型別的 wrapper class (Integer, Double, Long, Short, Byte, Float, Character, Boolean)

Optional 實例化:

要用下列內建的工廠方法

  • empty()
  • of(T value)
  • ofNullable(T value)

其中 ofNullable(T value)
如果 null, 就回傳 Optional.empty(), 否則回傳 Optional.of(value)
就像這段程式碼一樣

1
2
3
4
5
6
7
8
9
public static <T> Optional<T> createOptional(T value) {

// 方法一
// return value == null ? Optional.empty() : Optional.of(value);

// 方法二
// 用下面的方式可以很簡單的達到上面一樣的寫法
return Optional.ofNullable(value);
}

另外
OptionalInt, OptionalLOng, OptionalDouble 因為參數吃的是基本型態, 所以不會有 null
所以只有 of() 方法

  • static OptionalInt of(int value)
  • static OptionalLong of(long value)
  • static OptionalDouble of(double value)

而且 getter 會是 getAsInt(), getAsLong(), getAsDouble(), 不是使用 get()

Optional 取值

Optional 取值的時候, 要確定有值才能用 get(), 不然會丟出 NoSuchElementException

像是這樣

1
Exception in thread "main" java.util.NoSuchElementException: No value present

使用上的方法

  • orElse(T other) 一定會建立新物件

  • orElseGet(Supplier …) 必要時建立新物件

兩者作用差不多, 差在 orElseGet 用的是 Supplier, 只會在 Optional 空的時候才會執行, orElse 一定會建立新物件

  • orElseThrow(Supplier …) 空時, 丟一個 exception

  • ifPresent(Consumer…): Optional 有值時執行 Consumer 的內容 (Consumer 是 void 的, 所以這裡只印出來…)

  • ifPresentOrElse()

例子

找第一個奇數長度的字串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Optional<String> firstOdd = 
Stream.of("1234", "123456", "12")
.filter(s -> s.length()%2 != 0)
.findFirst();

System.out.println(firstOdd.get()); // NoSuchElementException

// 使用 orElse() 處理 取不到值 時該回什麼

System.out.println(firstOdd.orElse("no odd length string"));

System.out.println(firstOdd.orElseGet(() -> "no odd length string"));

System.out.println(firstOdd.orElseThrow(() -> new NoSuchElementException()));

System.out.println(firstOdd.orElseThrow(NoSuchElementException::new));

firstOdd.ifPresent(val -> System.out.println("find it!"));

Reference

  • Modern Java Recipes