モダンなJavaの書き方④:Optional

NullPointerException を防ぐ null 安全なコードの書き方を学びます。

モダン 1 2 3 4

モダンな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 に値が入っているかどうかを確認するメソッドです。

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

値の取得

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
生成された値
値がありません

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
値がありません

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
短すぎます

実践的なサンプル: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
不明
不明
まとめ
Optional.ofNullable() で null かもしれない値を包む
orElse() / orElseGet() でデフォルト値を指定する
ifPresent() で値がある場合のみ処理を実行する
map() / filter() でメソッドチェーンを使って変換・絞り込みができる
・メソッドの戻り値に使い、null の代わりに Optional.empty() を返すと null 安全になる