2018年4月16日月曜日

[JavaSE8 Goldへの道] その9 Optionalクラス

JavaSE8 Goldへの道(Upgrade to Java SE 8 Programmer 1Z0-810 試験対策)9回目です。

一連の記事は「JavaSE8Gold」ラベルを付けていきます。

今回はOptionalクラスです。


Optional<T>クラス

java.util.Optional<T>クラスは、nullかもしれない値を格納するコンテナクラスです。

Optionalインスタンスの生成

コンストラクタは利用できません。必ずファクトリメソッドを使います。
//空のOptionalを生成
Optional<String> str = Optional.empty();

//nullでない値で生成
//※nullを渡すとNullPointerException
Optional<String> optStr = Optional.of("test");

//nullかもしれない値で生成
String str = null;
Optional<String> optStr = Optional.ofNullable(str);

2つめのOptional#ofメソッドでは、nullを渡すとNullPointerExceptionがスローされます。
したがって基本はemptyofNullableメソッドで生成することになると思います。

Optionalから値の取得

値の取得はいろいろな方法があります。
//単に取得
//※中身がnullだとNoSuchElementException
String str = op.get();

//nullの場合渡した値を返す
String str = op.orElse("default");

//nullの場合渡したラムダ式(Supplier)の結果を返す
String str = op.orElseGet(() -> "default");

//nullの場合渡したラムダ式(Supplier)が返す例外をスローする
String str = op.orElseThrow(() -> new RuntimeException("ぬるぽ"));
1つ目はもし中身がnullの場合NoSuchElementExceptionがスローされるので、あまり出番はないと思います。

2つ目のorElseメソッドはnullを指定することもできます。

3つ目のorElseGetメソッドではラムダ式を渡せるので、もっと複雑な生成ロジックやを使うことができます。
(例えば文字列連結などをする場合、nullのときしか評価されないのでパフォーマンスへの影響が少なくなります)

4つ目のorElseThrowは中身がnullの場合なにか別の例外をスローしたい場合に使えます。

その他判定など

他に判定用のメソッドもあります。
//中身が存在するかどうか
boolean present = op.ifPresent();

//中身が存在する場合、ラムダ式(Consumer)を実行する
op.isPresent(s -> System.out.println(s));

1つ目のifPresentメソッドは要するにobj != nullと同じなので、Optionalを使う意味がありません。
したがって2つ目のisPresentメソッドが最も基本の使い方になると思います。

なにかメソッドを設計したときに、nullが返される可能性がある場合はOptional型にしておくと、使う側で工夫が可能になります。
・・・とはいえ、JavaAPIでも全てのnullを返す可能性があるメソッドがOptionalに置き換わったわけではないので、まだまだnullチェックが全く必要なくなったわけではありません。


Streamのような操作

※ここから先は試験範囲外かもしれません。

OptionalにはStreamと同じメソッドもいくつか定義されています。
//値の置き換え(map)
Optional<String> optStr = Optional.ofNullable("value");
Optional<Integer> optInt = optStr.map(s -> s.length());

//値の置き換え+ラップの解除(flatMap)
//なにか処理をしてOptional型を返すfuncメソッドを呼ぶ
Optional<String> optStr = Optional.ofNullable("value");
Optional<String> optStr2 = optStr.flatMap(s -> func(s));

//値のフィルタリング(filter)
Optional<String> optStr = Optional.ofNullable("value");
Optional<String> optStr2 = optStr.filter(s -> s.startWith("a"));

全て中身が存在する場合のみラムダ式が実行されます。存在しない場合空のOptionalが返されるだけです。

Optional#flatMapStreamも同じなのですが、ラムダ式の結果二重にラップされてしまう(Optional<Optional<T>>になってしまう)場合に、ラップを1つ解除してくれます。

Optional#filterはフィルタリングの結果falseになった場合は空のOptionalになります。

Streamの終端操作にはOptionalを返すものがいくつかありますが、これらのメソッドを使うとそのまま延長した処理のように書くことができます。
OptionalStreamの要素が0または1のものとも言えなくもない、と誰かが書いてた気がします。

より詳しくは以下の記事がわかりやすいです。


プリミティブ用Optional

Optionalにもプリミティブ型に対応したものが用意されています。
  • OptionalInt
  • OptionalLong
  • OptionalDouble
getメソッドだけ異なり、getAsXxxとなっています。前述の通り使う機会はないと思いますが。
また、nullがないためofNullableメソッドがありません。

プリミティブ型Streamの終端操作の一部で戻り値として使われています。


と、い、う、わ、け、で

メソッドの戻り値としてnullの可能性があるケースでは積極的に使っていきたいですね。
今後追加されるAPIでも使われると思うので、どのような操作があるかは覚えておきましょう。

今回も以下のサイトとGoldの通常試験の参考書を参考にしています。
一連の記事は「JavaSE8Gold」ラベルを付けていきます。

それではみなさまよきガジェットライフを(´∀`)ノ


▼こちらの記事もどうぞ

▼ブログを気に入っていただけたらRSS登録をお願いします!
▼ブログランキング参加中!応援よろしくお願いします。

スポンサーリンク