モダンなJavaの書き方④:Optional
Optional とは
Optional<T> は Java 8 で導入されたクラスで、「値が存在するかもしれないし、存在しないかもしれない」という状態を表現します。null を直接扱う代わりに Optional を使うことで、NullPointerException を防ぐ null 安全なコードを書けます。
Optional はメソッドの戻り値として使うのが主な用途です。フィールド変数や引数には使わないのが一般的なプラクティスです。
Optional の生成
Optional を生成する方法は3つあります。
Java
import java.util.Optional;
// 値が必ず存在する場合(nullを渡すとNullPointerException)
Optional<String> opt1 = Optional.of("Hello");
// 値がnullかもしれない場合(nullならemptyになる)
String value = null;
Optional<String> opt2 = Optional.ofNullable(value);
// 値が存在しない(空の)Optional
Optional<String> opt3 = Optional.empty();
System.out.println(opt1); // Optional[Hello]
System.out.println(opt2); // Optional.empty
System.out.println(opt3); // Optional.empty
実行結果
Optional[Hello]
Optional.empty
Optional.empty
Optional.empty
Optional.empty
値の存在確認
Optional に値が入っているかどうかを確認するメソッドです。
Java
Optional<String> opt = Optional.of("Java");
// 値が存在するか確認
System.out.println(opt.isPresent()); // true
System.out.println(opt.isEmpty()); // false(Java 11以降)
Optional<String> empty = Optional.empty();
System.out.println(empty.isPresent()); // false
System.out.println(empty.isEmpty()); // true
実行結果
true
false
false
true
false
false
true
値の取得
Optional から値を取り出すメソッドです。
| メソッド | 説明 |
|---|---|
| get() | 値を取得する。値がなければ NoSuchElementException |
| orElse(other) | 値があればその値、なければ other を返す |
| orElseGet(supplier) | 値がなければ Supplier を実行して返す |
| orElseThrow() | 値がなければ NoSuchElementException をスロー |
| orElseThrow(supplier) | 値がなければ指定した例外をスロー |
Java
Optional<String> opt = Optional.of("Java");
Optional<String> empty = Optional.empty();
// orElse:値がなければデフォルト値を返す
System.out.println(opt.orElse("default")); // Java
System.out.println(empty.orElse("default")); // default
// orElseGet:値がなければSupplierを実行
System.out.println(empty.orElseGet(() -> "生成された値")); // 生成された値
// orElseThrow:値がなければ例外をスロー
try {
empty.orElseThrow(() -> new IllegalStateException("値がありません"));
} catch (IllegalStateException e) {
System.out.println(e.getMessage()); // 値がありません
}
実行結果
Java
default
生成された値
値がありません
default
生成された値
値がありません
ifPresent・ifPresentOrElse
値が存在する場合にのみ処理を実行します。
Java
Optional<String> opt = Optional.of("Java");
Optional<String> empty = Optional.empty();
// ifPresent:値がある場合のみ実行
opt.ifPresent(v -> System.out.println("値: " + v)); // 値: Java
empty.ifPresent(v -> System.out.println("値: " + v)); // 何も出力されない
// ifPresentOrElse:値がある場合とない場合で処理を分ける(Java 9以降)
opt.ifPresentOrElse(
v -> System.out.println("存在します: " + v),
() -> System.out.println("値がありません")
);
empty.ifPresentOrElse(
v -> System.out.println("存在します: " + v),
() -> System.out.println("値がありません")
);
実行結果
値: Java
存在します: Java
値がありません
存在します: Java
値がありません
map・filter による変換と絞り込み
Optional の値を変換したり条件でフィルタリングできます。Stream API と似た感覚で使えます。
Java
Optional<String> opt = Optional.of(" hello ");
// map:値を変換する(空の場合はemptyのまま)
Optional<String> upper = opt.map(s -> s.trim().toUpperCase());
System.out.println(upper.orElse("")); // HELLO
// filter:条件を満たさない場合はemptyになる
Optional<String> longWord = opt
.map(String::trim)
.filter(s -> s.length() >= 5);
System.out.println(longWord.orElse("短すぎます")); // hello
Optional<String> shortOpt = Optional.of("hi");
Optional<String> filtered = shortOpt.filter(s -> s.length() >= 5);
System.out.println(filtered.orElse("短すぎます")); // 短すぎます
実行結果
HELLO
hello
短すぎます
hello
短すぎます
実践的なサンプル:null チェックの改善
Optional を使う前後のコードを比較します。
Java(Optional を使わない場合)
// ユーザー名の最初の文字を大文字で取得するメソッド
public static String getFirstLetterUpper(String name) {
if (name != null && !name.isEmpty()) {
return name.substring(0, 1).toUpperCase();
}
return "不明";
}
Java(Optional を使った場合)
import java.util.Optional;
// Optionalを使ったメソッド
public static String getFirstLetterUpper(String name) {
return Optional.ofNullable(name)
.filter(s -> !s.isEmpty())
.map(s -> s.substring(0, 1).toUpperCase())
.orElse("不明");
}
// 使用例
System.out.println(getFirstLetterUpper("tanaka")); // T
System.out.println(getFirstLetterUpper("")); // 不明
System.out.println(getFirstLetterUpper(null)); // 不明
実行結果
T
不明
不明
不明
不明
まとめ
・
・
・
・
・メソッドの戻り値に使い、null の代わりに Optional.empty() を返すと null 安全になる
・
Optional.ofNullable() で null かもしれない値を包む・
orElse() / orElseGet() でデフォルト値を指定する・
ifPresent() で値がある場合のみ処理を実行する・
map() / filter() でメソッドチェーンを使って変換・絞り込みができる・メソッドの戻り値に使い、null の代わりに Optional.empty() を返すと null 安全になる