JavaSE8から加わったOptional。
ストリームAPIやラムダ式に隠れてしまっているがとても便利。

ただ慣れてないと使いにくい部分はあるので、
使い方をまとめる。

Optionalがよくわからない場合は
こちら→Optional - nullを扱う新たな方法

生成

3つのファクトリメソッドが用意されているので
いずれかを使う。

値がnullであっても空のOptionalにすればよいので、
基本的にはofNullable()、empty()を中心に考える。

メソッド 概要
of() nullの場合、NullPointerExceptionになる。
ofNullable() nullの場合、空のOptionalを返す。
empty() 空のOptionalを返す。

Immutableなので1度中身のオブジェクトを決めてしまったら
入れ替えるのではなく、新たに生成する。

中身のオブジェクトの取得

Optionalから中身のオブジェクトを取得する場合、
それがnullだったときの方針を決めておかないと
うまく取得することができない。

代替値を設定するや例外を投げるといった方針によって、
メソッドを選ぶ。

アンチパターン

JavaDocを見るとget()が目につくので、
isPresent() + get() とやりたくなってしまうが
これをやってしまってはnullチェックしているのと大差ない。

Optional#orElse(null)とする方がマシ。
まずはorElseXXX()の中から検討する。

例 isPresent() + get()はなるべく避ける
String value = null;
Optional<String> optional = Optional.ofNullable(value);
 
String displayValue = null;
if (optional.isPresent())
    displayValue = optional.get();

orElse()

中身がnullの場合でも、そのときの値を指定できる。
orElse(null)も可。

String value = null;
Optional<String> optionalValue = Optional.ofNullable(value);
String displayValue = optionalValue.orElse("(デフォルト)");

orElseGet()

orElseとほぼ同じであるが、(ファクトリ)関数オブジェクトを指定できる。
(毎回違うインスタンスを生成できる)

String value = null;
Optional<String> optionalValue = Optional.ofNullable(value);
String displayValue = optionalValue.orElseGet(() -> "(デフォルト)");

orElseThrow()

中身がnullの場合、生成した例外をスローする。

String value = null;
Optional<String> optionalValue = Optional.ofNullable(value);
String displayValue = optionalValue.orElseThrow(RuntimeException::new);

中身のオブジェクトのメソッドを実行する

中身をいちいち取得しなくても、
中身のオブジェクトのメソッドを実行できる。

ifPresent()

consumerを渡すことで、副作用をともなう処理ができる。
もちろん、中身がnullの場合は何も行われないだけ。

isPresent()とメソッド名が酷似しているので注意。

String value = null;
Optional<String> optionalValue = Optional.ofNullable(value);
optionalValue.ifPresent(System.out::println);

中身のオブジェクトが保持しているオブジェクト(値)を
取得する

例えば、つぎのような場合

Optional_サンプルクラス図

これも中身のオブジェクトを取得しなくても、
処理することができる。

map()

戻り値がOptionalなので、
そのOptionalに対してまたメソッドを実行できる。

Person person = new Person("田中", "tanaka@example.com", Optional.empty());
Optional<Person> optionalPerson = Optional.ofNullable(person);
 
Optional<String> name = optionalPerson.map(Person::getName);
String displayedName = name.orElse("(未登録)");

flatMap()

mapとの違いは、保持しているオブジェクトがOptionalの場合
そのまま取り出せるというところ。

Person person = new Person("田中", "tanaka@example.com", Optional.empty());
Optional<Person> optionalPerson = Optional.ofNullable(person);
 
Optional<String> twitterAccount = optionalPerson.flatMap(Person::getTwitterAccount);
String displayeTtwitterAccount = twitterAccount.orElse("(未登録)");

PR