Optional.filter を一言でいうと
Optional.filter は
「中身が“あるときだけ”条件をチェックし、条件を満たさなければ空にしてしまう」
ためのメソッドです。
Stream の filter と同じ発想で、Optional 版の「条件によるふるい落とし」と思ってください。if (x != null && 条件) というお決まりパターンを、optional.filter(条件) という形で書けるようにするための道具です。
まずは「null チェック版」との対比でイメージする
null で書くとどうなるか
「ユーザーがいるかもしれないし、いないかもしれない。
さらに、18 歳以上なら処理したい」という状況を考えます。
User user = findUserOrNull(); // 見つからなければ null
if (user != null && user.getAge() >= 18) {
System.out.println(user.getName() + " は成人です");
}
Javaここでは、
userが null かもしれないのでチェックする- null でなければ年齢を見て、18 歳以上なら処理する
という二段階の if になっています。
Optional.filter で書くとどうなるか
同じことを Optional で書き直します。
Optional<User> maybeUser = findUser(); // 見つからなければ Optional.empty()
maybeUser
.filter(u -> u.getAge() >= 18)
.ifPresent(u -> System.out.println(u.getName() + " は成人です"));
Javaここで起きていることは、さっきの if とまったく同じです。
中身がなければ、filter は何もせずにそのまま空。
中身があれば、u -> u.getAge() >= 18 を評価し、true ならそのまま、false なら空にする。
「null じゃないか?」と「条件を満たすか?」を、if (user != null && 条件) ではなくoptional.filter(条件)
という 1 ステップにまとめているイメージです。
Optional.filter の動きを丁寧に分解する
中身がある & 条件を満たす場合
Optional<Integer> maybeNum = Optional.of(10);
Optional<Integer> result =
maybeNum.filter(n -> n > 5);
System.out.println(result); // Optional[10]
Java流れはこうです。
- 中身 10 を取り出す
n > 5を評価 → true- 元の値 10 をそのまま
Optional.of(10)として返す
中身があるが、条件を満たさない場合
Optional<Integer> maybeNum = Optional.of(3);
Optional<Integer> result =
maybeNum.filter(n -> n > 5);
System.out.println(result); // Optional.empty
Java流れはこうです。
- 中身 3 を取り出す
n > 5を評価 → falseOptional.empty()を返す(中身を捨てる)
そもそも中身がない場合
Optional<Integer> maybeNum = Optional.empty();
Optional<Integer> result =
maybeNum.filter(n -> n > 5);
System.out.println(result); // Optional.empty
Java中身がないので、条件は評価されません。
そのまま Optional.empty() が返ってきます。
ここで一番大事なのは、
「中身がないときに、わざわざ何かする必要がない」
「“あるときだけ”条件をチェックしてくれる」
という性質です。
これのおかげで、if (x != null && 条件) を書かなくてよくなります。
filter を挟むと「安全なパイプライン」が作れる
map と組み合わせる例
「ユーザーがいて、かつアクティブなユーザーなら、そのメールアドレスを取り出したい」というケースを考えます。
Optional<User> maybeUser = findUser();
Optional<String> maybeEmail =
maybeUser
.filter(User::isActive) // アクティブなユーザーだけ通す
.map(User::getEmail); // メールアドレスに変換
Javaここでの動きはこうです。
ユーザーがいない → 最初から empty のまま
ユーザーはいるが非アクティブ → filter で empty になる
アクティブユーザーがいる → map でメールアドレスに変換される
「条件を満たさないなら、その時点で“なかったこと”にする」という設計が、filter 1 行で表現できます。
そのまま orElse につなげる
さらに、「条件を満たすユーザーがいなければデフォルト値を使う」としたいなら、こう書けます。
String email =
findUser()
.filter(User::isActive)
.map(User::getEmail)
.orElse("no-reply@example.com");
Javafilter で「対象を絞る」、map で「形を変える」、orElse で「なかったときの扱いを決める」。
この三段構えが、Optional 設計の基本パターンの一つです。
「if で unwrap する」書き方からの卒業
やりがちなパターン
Optional を使い始めたときに一番やりがちなのが、こういう書き方です。
Optional<User> maybeUser = findUser();
if (maybeUser.isPresent() && maybeUser.get().getAge() >= 18) {
User user = maybeUser.get();
System.out.println(user.getName() + " は成人です");
}
JavaisPresent() と get() をセットで使っていて、
せっかく Optional を導入したのに、結局「null 的な扱い」に戻ってしまっています。
filter + ifPresent に書き換える
同じことは、こう書けます。
findUser()
.filter(u -> u.getAge() >= 18)
.ifPresent(u -> System.out.println(u.getName() + " は成人です"));
JavaisPresent / get コンボをやめて、
「条件でふるいにかける」→ filter
「あるときだけ何かする」→ ifPresent
という役割分担にすると、コードの意図が一気に読みやすくなります。
Stream.filter との対応で理解を深める
Stream.filter とほぼ同じ感覚で使える
Stream の filter はこうでした。
List<Integer> evens =
nums.stream()
.filter(n -> n % 2 == 0)
.toList();
Java「条件を満たす要素だけを通す」という意味です。
Optional の filter も、まったく同じ発想です。
Optional<Integer> maybeEven =
maybeNum.filter(n -> n % 2 == 0);
Java違いは、「要素が 0 個か 1 個か」というだけ。
Stream は「0 個以上の要素の流れ」
Optional は「0 個か 1 個の要素の箱」
なので、filter の意味もそのままスライドして考えられます。
Optional.filter を設計にどう活かすか
「条件を満たさないなら“なかったこと”にする」場面で使う
filter が本領を発揮するのは、
「値はあるけれど、この条件を満たさないなら“なかったこと”にしたい」
という場面です。
例えば、
ログイン中のユーザーがいるかもしれない
いても、BAN されているユーザーなら扱いたくない
というときは、こう書けます。
Optional<User> maybeActiveUser =
getCurrentUser()
.filter(user -> !user.isBanned());
JavagetCurrentUser() が「ログイン中のユーザー(いなければ empty)」を返し、filter が「BAN されていないユーザーだけ通す」。
結果として、
ログインしていない → empty
ログインしているが BAN → empty
ログインしていて BAN されていない → そのユーザーが入った Optional
という状態になります。
「条件チェックを if ではなく“宣言”として書く」
null ベースだと、どうしても if が増えます。
User user = getCurrentUserOrNull();
if (user != null && !user.isBanned()) {
// ここでだけ user を使う
}
JavaOptional ベースだと、条件チェックも「宣言的」に書けます。
getCurrentUser()
.filter(user -> !user.isBanned())
.ifPresent(user -> {
// ここでだけ user を使う
});
Java「ログイン中で、かつ BAN されていないユーザーが“いるなら”この処理をする」
という意図が、そのままコードの形になります。
まとめ:Optional.filter を自分の言葉で説明するなら
あなたの言葉で Optional.filter を説明するなら、こうです。
「Optional.filter は、“中身があるときだけ条件をチェックし、条件を満たさなければ Optional を空にしてしまう”メソッド。if (x != null && 条件) というパターンを、optional.filter(条件) として安全かつ読みやすく書けるようにするための道具で、map や orElse と組み合わせると、“あるかもしれない値”に対する条件分岐を宣言的に表現できる。」
