Java | Java 標準ライブラリ:Optional の目的

Java Java
スポンサーリンク

Optional は「null をそのまま渡さないための箱」

java.util.Optional は、簡単に言うと
「あるかもしれないし、ないかもしれない値」を入れて渡すための“箱”です。

それだけ聞くと
「じゃあ null でよくない?」
と思うかもしれませんが、Optional の目的は

「ただ無いことを表す」のではなく、
「無い可能性があることを、コード上で“見える化”する」

ところにあります。

Optional<T> を返すメソッドは「T が返ってこないこともあるよ」と宣言しています。
これにより、呼び出し側は

「この結果は、必ずあるとは限らない。ちゃんと無いケースを処理しよう」

と、自然に意識せざるを得なくなります。
ここが、null をそのまま返すのとの決定的な違いです。


null の何がそんなにまずいのか(Optional が生まれた背景)

「書いた本人も忘れる“隠れ仕様”になる」のが null

例えば、こんなメソッドを考えます。

public User findUser(String id) {
    // 見つからなかったら null を返すかもしれない
}
Java

こういうメソッドは世の中に山ほどあります。
呼び出す側は、ちゃんと null を想定して書かないといけません。

User user = findUser("U001");
if (user != null) {
    System.out.println(user.getName());
}
Java

けれど現実には、忙しい中で人はよくこう書きます。

User user = findUser("U001");
System.out.println(user.getName());   // 見つからなかったとき NPE(NullPointerException)
Java

「たぶんいるでしょ」「まあ今回は大丈夫だろう」と思って null チェックを抜かす。
それが、よくある NullPointerException の温床です。

問題は、「このメソッドは null を返すことがある」という情報が、
メソッド名や戻り値の型だけでは全く伝わらないことです。

  • findUser という名前だけでは「必ず見つかる」のか「見つからないかも」なのか分からない
  • 戻り値の型は User なので、「null があり得る」とは読み取れない

仕様書やコメントに書けと言われても、
コードをザッと読むだけでは伝わりません。

Optional は「無い可能性」を型で表す

ここで Optional の登場です。
さきほどのメソッドの戻り値を、こう変えます。

import java.util.Optional;

public Optional<User> findUser(String id) {
    // ユーザが見つかるかもしれないし、見つからないかもしれない
}
Java

呼び出し側は、こう書くことになります。

Optional<User> optUser = findUser("U001");
Java

この時点で、

「あ、このメソッドの結果は“無いかも”なんだな」

と、型だけで分かります。
Optional<User> は「User かもしれないし、無いかもしれない」という「意味付きの型」です。

User だけを返していたときは、仕様を覚えていないと null チェックを忘れます。
Optional<User> になった瞬間、「無い可能性」を無視できなくなる。
これが Optional のいちばん大きな目的です。


Optional を使うと、呼び出し側の書き方が変わる

「あるときだけ処理する」が自然に書ける

例えば、「ユーザが見つかったときだけ名前を表示する」場合、
Optional ならこう書けます。

findUser("U001").ifPresent(user -> {
    System.out.println(user.getName());
});
Java

ifPresent は、「中身が存在すれば、その値を引数にラムダを実行する」メソッドです。

「無いかもしれない」ことを意識せざるを得ない形になっているので、
null のときにうっかり getName() を呼んでしまう、という事故がかなり減ります。

「見つからなかったときのデフォルト値」を一緒に書ける

例えば、「ユーザ名を取りたい。いなければ "ゲスト" にしたい」というケース。

null パターンだと、こうなりがちです。

User user = findUser(id);   // null の可能性
String name;
if (user != null) {
    name = user.getName();
} else {
    name = "ゲスト";
}
Java

Optional なら、こう書けます。

String name = findUser(id)
        .map(User::getName)
        .orElse("ゲスト");
Java

意味としては:

  • map(User::getName) で、User がいれば getName() を適用して Optional<String> に変換
  • orElse("ゲスト") で、無い場合の代替値を指定

「無いときどうするか」を型にぶら下がったメソッドで表現できるので、
if (user != null) のような「うっかり忘れやすいチェック」から脱出できます。


Optional が目指しているものは「NPE を減らす」だけではない

「無いことを真剣に設計に取り込む」

Optional の本当の目的は、

「値が存在しないというケースを、“例外的なもの”ではなく、“普通にあり得る状態”として設計に取り込む」

ことです。

null は簡単に投げられます。
戻り値を null にするのも、フィールドを null のまま放置するのも、一瞬です。
そして、その「無いかも」がコード上では見えない。
後から読む人は、仕様を全部頭に入れていないと、どこで null が来るか分からない。

Optional を返せば、「このメソッドは値が無いことがある」という情報が型として固定されるので、
呼び出し側は「無いケース」を真剣に考えざるを得ません。

  • ないならデフォルト値にするのか
  • ないなら例外を投げるのか
  • ないなら何もしないのか

「無い可能性」を設計に引きずり出して、
足りないところを明示的に埋めていく。
この意識づけのための道具が Optional です。


Optional を「いつ使うか」「どこに使わないか」

戻り値に使う:特に「見つからないことがある検索系」

Optional は、戻り値で使うのが基本です。
よくあるのは「検索系メソッド」です。

Optional<User> findById(String id);
Optional<Order> findLatestOrder(String userId);
Optional<String> findNickname(String userId);
Java

「存在しないことが普通にありえる」 API では、
戻り値を Optional にすると意図がとても伝わりやすくなります。

フィールドや引数には「基本使わない」

一方で、Optional は

  • フィールドとして持つ
  • メソッドの引数として受け取る
  • コレクションの要素として大量に持つ

といった使い方は、原則として推奨されていません。

// 悪い例(と言われがち)
class User {
    private Optional<String> nickname;  // フィールドに Optional
}
Java

こういう場合は、設計を見直したほうがよいことが多いです。

  • 「ニックネームが必須ではない」という仕様なら、フィールドは null を許容する or 別のラッパークラスで表現する
  • 「デシリアライズの都合」などでどうしても null が来るなら、外側で Optional に包んで扱う

Optional の本来の目的は「API の境界で、“無い可能性”を型として表現する」ことなので、
内側の実装(フィールドや引数)まで Optional だらけにすると、
かえって読みにくくなることが多いです。


「Optional を使えばすべての null 問題が解決する」わけではない

Optional を返す側も、結局は null を扱うことがある

当然ですが、Optional を返すメソッドの中では、
内部的に null を扱っていることも多いです。

public Optional<User> findUser(String id) {
    User user = findUserFromDb(id); // ここは null かもしれない
    return Optional.ofNullable(user);
}
Java

Optional.ofNullable(user) を使うことで、
user が null なら Optional.empty() に変換して返しているだけです。

つまり、Optional は「プログラムの内部から null を完全に追放する魔法」ではありません。
あくまで「外から見えるインターフェースに、null を直に出さない」ためのラッパーです。

「何でも Optional にしておけばいい」ではない

極端な例として、

Optional<String> getName();
Optional<Integer> getAge();
Optional<List<Order>> getOrders();
Java

のように、あらゆる getter の戻り値を Optional にしてしまうと、
呼び出し側は .get().get().get() のような連鎖になってしまったりします。

Optional の使いすぎは、逆にコードを読みにくくします。
大事なのは、「どこに無さが存在し得るのか」を設計として整理した上で、

  • 特に外に見せたい境界(API)にだけ Optional を使う
  • 内部は別の手段(不変オブジェクト、デフォルト値、バリデーション)で整える

というバランス感覚です。


まとめ:初心者として Optional の“目的”をどう掴むか

Optional の目的を、一言でまとめるならこうです。

「null をなくす」のではなく
「無い可能性を“型として見える化”して、呼び出し側にちゃんと考えさせる」

より具体的に言うと、

戻り値に Optional を使うことで、「この結果は無いかもしれない」という情報を型に乗せる
呼び出し側は、ifPresentorElse などを通じて、「無い場合どうするか」を必ずどこかで決める
その結果、なんとなく null を返して、なんとなく使って、なんとなく NPE、という事故を減らせる

という役割を担っています。

タイトルとURLをコピーしました