Optioal<T> は Java 8 から導入されたクラスです。簡単に説明すると、値をひとつ保持しているか、もしくは値が無いことを表すクラスです。他のプログラミング言語では、SML/NJ や OCaml などの Option 型、Haskell の Maybe、Scala の Option クラスに相当する機能です。
たとえば、データを探索するメソッドで、データが見つからなかったときに null を返すよりも、Optional<T> を使ったほうが安全なプログラムを作ることができます。また、ジェネリクスのほかにも int, long, double 専用の Optional も用意されています。
Optional<T> のインスタンスはスタティックメソッドで生成します。
1 は引数 e を格納したインスタンスを生成します。null 以外の値であることが確実な場合に使います。2 は引数 e が null の場合は値がないことを表すインスタンス (empty) を、そうでなければ値を格納したインスタンスを生成します。3 は文字通りの意味で、empty を生成します。
簡単な例を示しましょう。
jshell> Optional.of(123) $1 ==> Optional[123] jshell> Optional.of(1.2345) $2 ==> Optional[1.2345] jshell> Optional.ofNullable(456) $3 ==> Optional[456] jshell> Optional.ofNullable(null) $4 ==> Optional.empty jshell> Optional.empty() $5 ==> Optional.empty
値の有無はメソッド isPresent() で調べることができます。値は get() で取得することができますが、empty に get() を適用すると NoSuchElementException という例外が送出されます。これでは null と変わらないので不便ですね。値を取得する、もしくはメソッドに渡したい場合は、次のメソッドを使うと便利です。
簡単な例を示しましょう。
jshell> var a = Optional.of(123) a ==> Optional[123] jshell> a.orElse(0) $7 ==> 123 jshell> a.ifPresent(System.out::println) 123 jshell> var b = Optional.empty() b ==> Optional.empty jshell> b.orElse(0) $11 ==> 0 jshell> b.ifPresent(System.out::println)
最後の ifPresent() は Optional に値がないので、println() は実行されません。
Optional のデータを処理する場合、その結果を Optional に包んで返したい場合があります。このような場合、いちいちデータを取り出すのは面倒なので、Optional のままデータを処理できると便利です。Optional<T> には、このような処理に適した高階関数が用意されています。
flatMap() の使い方がちょっと難しいかもしれません。たとえば、map() の引数に map() を渡すことを考えてみましょう。この場合、引数の map() の返り値は Optional になり、それを Optional に包んで返すので、返り値のデータ型は Optional<Optional<T>> になってしまいます。つまり Optional が二重になってしまうのです。このような場合、flatMap() を使うと便利です。
Lisp などの関数型言語や論理型言語の Prolog など、リストを操作するプログラミング言語では、リストの中にリストを格納することができます。二重になったリストをフラットなリストに変換する操作を「平坦化 (flattening)」といいます。このような処理を行う関数は、多くの処理系で flatten という名前で定義されています。
簡単な例を示しましょう。
jshell> var a = Optional.of(1234) a ==> Optional[1234] jshell> a.filter(x -> x % 2 == 0) $2 ==> Optional[1234] jshell> a.filter(x -> x % 2 != 0) $3 ==> Optional.empty jshell> a.map(x -> x * x) $4 ==> Optional[1522756] jshell> var b = a.map(x -> x * x) b ==> Optional[1522756] jshell> a.flatMap(x -> b.map(y -> x + y)) $6 ==> Optional[1523990]
一番最後の例は、a または b が empty の場合でも結果は empty になります。このように、高階関数を使うと Optional のまま処理することができます。また、これらのメソッドをつないで処理した場合、Optional ならば途中で値が empty になったとしても、最終結果は empty になります。
つまり、Optional のメソッドチェインでは、途中で empty をチェックする必要がないのです。その分だけプログラムを簡単に記述することができます。Haskell の Maybe や Either のように、簡単なエラー処理に Optional を使うことができるかもしれません。興味のある方はいろいろ試してみてください。