Optionalとは?
JavaのOptionalクラスは、値がnullである可能性がある場合に値をラップするためのクラスです。
例えば、Mapから値を取得する際に値が存在しない場合、値はnullになります。
Optionalクラスを使用すると、値がnullかどうかを簡単に確認し、nullの場合はデフォルト値を設定できます。
つまりOptionalクラスを使うと、値がnullである可能性のあるコードを安全に扱うことができるようになります。
Java8から使用できるようになっているので、執筆時点においてはほとんどの開発環境で利用可能になっているはずです。
どういった場合に使うのか?
Optionalクラスは、値がnullである可能性のあるメソッドの戻り値としてよく使用されます。
例えば、Mapから値を取得するメソッドを作成する場合、そのメソッドの戻り値をOptional型にします。
これにより、呼び出し側は値がnullかどうかを簡単に確認し、nullの場合はデフォルト値を設定できるようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class Main { // Map private static Map<Integer, String> map = new HashMap<>(){ { put(1, "文字列"); } }; public static void main(String[] args) { // 取得 Optional<String> value = fromMap(2); // 判定およびデフォルト値 System.out.println(value.orElse("NULL")); // nullであるため、デフォルト値の"NULL"文字列が表示される } /** * Mapからkeyを元にvalueを取得するメソッド * @param key キー * @return value */ private static Optional<String> fromMap(Integer key) { return Optional.ofNullable(map.get(key)); } } |
使い方
Optionalクラスには、map、flatMap、filterなど、さまざまなメソッドが用意されており、これらのメソッドを使用して、Optionalオブジェクトを操作して値を変更できます。
例えば、Optionalオブジェクトにmapメソッドを適用すると、Optionalオブジェクトの値が指定された関数でマップされます。
しかし主な使い方は以下になることが多いでしょう。
Optionalを生成する
Optionalを使用するには、まずOptionalオブジェクトを作成する必要があります。
Optionalオブジェクトを作成するには、ofNullable()メソッド、またはof()メソッドを使用します。
ofNullable()メソッドは、値とnullを受け入れます。
値がnullでない場合は、Optionalオブジェクトが作成されます。
値がnullの場合は、空のOptionalオブジェクトが作成されます。
1 2 |
Optional<String> nullable = Optional.ofNullable("not null"); Optional<String> nullable2 = Optional.ofNullable(null); |
of()メソッドはnullではない値のみを受け入れ、Optionalオブジェクトを作成します。
もし、nullをof()メソッドで生成しようとした場合、NullPointerExceptionがスローされるので注意が必要です。
1 2 3 4 |
Optional<String> notNull = Optional.of("not null"); // NullPointerExceptionが発生する Optional<String> nullValue = Optional.of(null); |
定数をOptionalに格納する場合はof()メソッドで問題ありませんが、多くの場合は変数を格納することになるでしょう。
変数はnullである可能性があるため、特に理由が無い限りはofNullable()メソッドを使用してOptionalオブジェクトを生成するようにして下さい。
Nullであるか判定する
Optionalオブジェクトが作成されたら、isPresent()メソッドを使用して値が存在するかどうかを確認できます。
isPresent()メソッドがtrueを返す場合、値は存在します。isPresent()メソッドがfalseを返す場合、値は存在しません。
値が存在しないことを確認したい場合は、isEmpty()メソッドを使うこともできます。
isEmpty()メソッドがtrueを返す場合、値は存在しません。falseを返す場合、値は存在します。
1 2 3 4 5 6 7 8 9 10 11 |
Optional<String> nullable = Optional.ofNullable("not null"); if (nullable.isPresent()) { System.out.println("nullable : " + nullable.get()); } // nullable : not null Optional<String> nullable2 = Optional.ofNullable(null); if (nullable2.isEmpty()) { System.out.println("nullable2 : " + nullable2); } // nullable2 : Optional.empty |
値を取得する
値が存在する場合は、get()メソッドを使用して値を取得できます。
通常、get()メソッドを実行する前にisPresent()メソッドで値が存在するかの確認を行うべきです。
もし存在しない値をget()しようとするとNoSuchElementExceptionがスローされるので注意が必要です。
使用例は「Nullであるか判定する」のisPresent()メソッドのコードを参照して下さい。
デフォルト値を返す
値が存在しない場合、orElse()メソッドまたはorElseGet()メソッドを使用してデフォルト値を取得できます。
orElse()メソッドはOptionalオブジェクトが空でない場合は、Optionalオブジェクトの値が返されます。
Optionalオブジェクトが空の場合、デフォルト値が返されます。デフォルト値にはnullを与えることもできます。
1 2 3 4 5 6 7 8 9 |
Optional<String> nullable = Optional.ofNullable("not null"); System.out.println("nullable : " + nullable.orElse("default")); // nullable : not null Optional<String> nullable2 = Optional.ofNullable(null); System.out.println("nullable2 : " + nullable2.orElse("default")); System.out.println("nullable2 : " + nullable2.orElse(null)); // nullable2 : default // nullable2 : null |
orElseGet()メソッドはOptionalオブジェクトが空でない場合は、Optionalオブジェクトの値が返されます。
Optionalオブジェクトが空の場合、Supplierの実行結果が返されます。
1 2 3 4 5 6 7 8 9 10 11 |
Optional<String> nullable = Optional.ofNullable("not null"); Supplier<String> supplier = () -> "default"; System.out.println("nullable : " + nullable.orElseGet(supplier)); // nullable : not null Optional<String> nullable2 = Optional.ofNullable(null); System.out.println("nullable2 : " + nullable2.orElseGet(supplier)); // nullable : default // Supplierとしてnullを与えるとNullPointerExceptionがスローされるので注意する // System.out.println("nullable2 : " + nullable2.orElseGet(null)); |
Collection.stream()での使用例
ListやSetといったCollectionからstreamを使って任意の値を取得する以下のメソッドはOptionalを返すようになっています。
以下のように、任意のリストのうち最初の文字が”h”である最初の値を取得するとします。
1 2 3 4 5 6 |
List<String> lists = Arrays.asList("hoge", "huga", "piyo"); Optional<String> value = lists.stream() .filter(list -> list.startsWith("h")) .findFirst(); value.ifPresent(System.out::println); // (1) // hoge |
この例では”hoge”が該当するので値が取得されて表示されますが、該当する値が無ければ何も表示されません。
このようにCollection.streamの操作においてOptionalは押さえて置くべき技術です。
値がある場合だけ実行する
Optionalには値がある場合だけ、処理をさせることができるifPresent()メソッドがあります。
「Collection.stream()での使用例」の(1)を参照して下さい。
ここでは値がある場合はコンソールに出力され、もし値が無ければ処理は実行されません。
また、(1)は以下のコードと同等なので、if文を使わないことでより簡潔に書けるようになっています。
1 2 3 |
if(value.isPresent()) { System.out.println(value.get()); } |
まとめ
- OptionalはNullを安全に扱うためのクラスである。
- Optionalから値を取得する前にNullでないことを確認すべきである。
- Nullである場合にデフォルト値を返す仕組みが用意されている。
- ifPresent()メソッドを使うと分岐を簡潔に記述することができる。
Optionalを初めて使う場合、ちょっとした抵抗や違和感があるかもしれません。
しかし、Optionalを使うことでNullへの安全性が向上するはずです。
今後も利用される場面は増えていくと思いますので、しっかり使えるようになっておきたい技術です。
参考
【公式API】Java11 クラスOptional<T>
【公式API】インタフェースStream
コメント