【Java】OptionalによるNull検査
Optional とは
Optionalとは、Java8 で導入された仕組み(クラス)になります。
例えば以下のように、データを取得するメソッドがあるとします。
private Map<String, String> users = new HashMap<String, String>() {
{
put("0001", "Taro");
put("0002", "Jiro");
put("0003", "Saburo");
}
};
public String getName(String id) {
return users.get(id);
}getName()は、idに該当するデータがあればその値を返しますが、ない場合はnullを返します。
しかし、getName()の実装を知らなければ、これがnullを返すかどうかがわかりません。
知らずにgetName().toUpperCase()などとすると、NullPointerExceptionがスローされてしまいます。
これを明示的に「nullの可能性がある値」と示すのがOptionalになります。
Optionalから値を取得するには、「nullであるかを検査する」必要があります。
Optional の宣言
Optionalはジェネリクスになっており、検査したい値の型を設定します。
public Optional<String> getName(String id) {
//...
}値の設定
ofNullable
nullの可能性のある値を設定します。
public Optional<String> getName(String id) {
return Optional.ofNullable(users.get(id));
}of, empty
ofは、nullでない値を設定します。
値が null の場合は、NullPointerExceptionがスローされます。
emptyは、値としてnullを設定します。
public Optional<String>getName(String id) {
if (users.containsKey(id)) {
return Optional.of(users.get(id));
} else {
return Optional.empty();
}
}上記の例は、ofNullableの例と同じ結果になります。
値の取得・検査
get
Optionalに設定した値を取得します。
ただし値がnullの場合は、NoSuchElementExceptionがスローされます。
Optional<String> opt1 = getName("0001");
String name1 = opt1.get(); // "Taro"
Optional<String> opt2 = getName("0004");
String name2 = opt2.get(); // NoSuchElementExceptionisPresent
Optionalに設定した値がnullでないことを検査します。
Optional<String> opt = getName("0001");
if (opt.isPresent()) {
// nullでない場合
} else {
// nullの場合
}orElse
Optionalに設定した値がnullでなければその値を、nullであればorElseで設定した値を取得します。
Optional<String> opt1 = getName("0001");
String name1 = opt1.orElse("No User"); // "Taro"
Optional<String> opt2 = getName("0004");
String name2 = opt2.orElse("No User"); // "No User"orElseGet
考え方はorElseと同じですが、orElseGetにはラムダ式でどのような値を返すかの処理を設定します。
Optional<String> opt1 = getName("0001");
String name1 = opt1.orElseGet(() -> {
//値設定のための処理
return "No User"
}); // "Taro"
Optional<String> opt2 = getName("0004");
String name2 = opt2.orElseGet(() -> {
//値設定のための処理
return "No User"
}); // "No User"orElseThrow
Optionalに設定した値がnullでなければその値を取得し、nullであれば指定した例外をスローします。
Optional<String> opt1 = getName("0001");
String name1 = opt1.orElseThrow(() -> new RuntimeException()); // "Taro"
Optional<String> opt2 = getName("0004");
String name2 = opt2.orElseThrow(() -> new RuntimeException()); // RuntimeExceptionifPresent
Optionalに設定した値がnullでない場合の処理をラムダ式で設定します。
引数には設定した値が渡されます。
nullの場合は何も起きません。
Oprional<String> opt = getName("0001");
opt.ifPresent((e) -> {
//nullでない場合の処理
//e: "Taro"
});値の変換
filter
Optionalに設定した値に、ラムダ式で条件を設けます。
条件を満たす場合はその値が設定されたOptionalが、満たさない場合はnullを持つOptionalが返されます。
Optionalがnullの場合は、nullを持つOptionalが返されます。
Optional<String> opt10 = getName("0001");
Optional<String> opt11 = opt10.filter((e) -> e.length < 5); // Optional: "Taro"
Optional<String> opt12 = opt10.filter((e) -> e.length < 4); // Optional: null
Optional<String> opt20 = getName("0004");
Optional<String> opt21 = opt20.filter((e) -> e.length < 5); // Optional: nullmap
Optionalに設定した値を、ラムダ式で指定した値に変換します。
結果として、変換した値を持つOptionalを返します。
Optionalがnullの場合は、nullを持つOptionalが返されます。
Optional<String> opt10 = getName("0001");
Optional<String> opt11 = opt10.map((e) -> e.toUpperCase()); // Optional: "TARO"
Optional<String> opt20 = getName("0004");
Optional<String> opt21 = opt20.map((e) -> e.toUpperCase()); // Optional: nullflatMap
例えば、以下のようにmapでOptionalに変換しようとすると、ジェネリクスが入れ子になってしまいます。
Optional<String> opt10 = getName("0001");
Optional<Optional<String>> opt11 = opt10.map((e) -> Optional.ofNullable(e));これを解決するのがflatMapです。
Optional<String> opt10 = getName("0001");
Optional<String> opt11 = opt10.flatMap((e) -> Optional.ofNullable(e));実際にはこのような使い方はしませんが、どのようなものか覚えておきましょう。