Java | Java 詳細・モダン文法:設計・実務視点 – モダン構文の使い過ぎ問題

Java Java
スポンサーリンク

「モダン構文の使い過ぎ問題」とは何か

Java には、ラムダ式、Stream API、Optional、record、switch 式、パターンマッチングなど、
「モダンでカッコいい構文」がたくさんあります。

これ自体は素晴らしい進化ですが、
問題になるのは 「読みにくくなるほど盛り込みすぎる」 場合です。

つまり、
書いている本人は楽しいけれど、
後から読む人にとっては「何をしているのか全然頭に入ってこない」状態。
これが「モダン構文の使い過ぎ問題」です。


典型例:Stream 地獄と if 地獄の対比

素直な for 文の方が読みやすいケース

まず、シンプルな例から見てみましょう。

// 素直な for 文
int sum = 0;
for (var n : numbers) {
    if (n > 0) {
        sum += n;
    }
}
Java

これを「モダンに書こう」として、こうする人がいます。

// モダン構文を盛り込みすぎた例
int sum = numbers.stream()
        .filter(n -> n > 0)
        .mapToInt(Integer::intValue)
        .sum();
Java

この程度ならまだ読めますが、
条件が増えたり、ネストしたりすると一気にカオスになります。

int sum = numbers.stream()
        .filter(n -> n > 0)
        .filter(n -> n % 2 == 0)
        .map(n -> n * 2)
        .filter(n -> n < 100)
        .mapToInt(Integer::intValue)
        .sum();
Java

「何をしているか」は読めば分かりますが、
一行一行を頭の中でシミュレーションしないと理解できない という状態です。

同じ処理でも、場合によっては for 文の方が読みやすくなります。

int sum = 0;
for (var n : numbers) {
    if (n <= 0) continue;
    if (n % 2 != 0) continue;
    int doubled = n * 2;
    if (doubled >= 100) continue;
    sum += doubled;
}
Java

どちらが「読みやすいか」は、
チームのレベル・慣れ・処理の複雑さ によって変わります。


Optional の使い過ぎ問題

「null が嫌だから全部 Optional」にすると逆に読みにくくなる

Optional は「値がない可能性」を型で表現できる素晴らしい仕組みですが、
何でもかんでも Optional にすると、かえってコードが読みにくくなります。

// モダン構文を盛り込みすぎた例
Optional.ofNullable(user)
        .map(User::getAddress)
        .map(Address::getZipCode)
        .ifPresent(zip -> send(zip));
Java

これを見て「おお、イマドキっぽい」と感じるかもしれませんが、
初心者や別チームの人からすると、
「で、結局何してるの?」となりがちです。

同じ処理を、少し素直に書くとこうなります。

if (user == null) {
    return;
}
var address = user.getAddress();
if (address == null) {
    return;
}
var zip = address.getZipCode();
if (zip != null) {
    send(zip);
}
Java

こちらの方が、
「どの段階で null を弾いているか」が一目で分かります。

重要なのは「Optional を使うこと」ではなく、「意図が伝わること」です。


「モダン構文を使うべきところ」と「我慢すべきところ」

モダン構文がハマる場所

モダン構文が本当に力を発揮するのは、
「パターンがはっきりしている処理」です。

例えば、
コレクションのフィルタ・変換・集計、
条件分岐の整理、
値オブジェクトの表現(record)など。

// これは Stream が素直にハマる例
var names = users.stream()
        .filter(User::isActive)
        .map(User::getName)
        .toList();
Java

こういう「直線的な処理」は、
Stream の方が for 文よりも読みやすくなることが多いです。

我慢した方がいい場所

逆に、
ビジネスロジックが複雑で、
条件分岐が多く、
途中でログを出したり、例外を投げたりするような処理は、
無理に Stream や Optional で書かない方がいいです。

// 無理にモダン構文で書こうとした危険な例(イメージ)
orders.stream()
      .filter(o -> {
          if (!o.isValid()) {
              log.warn("invalid: {}", o);
              return false;
          }
          return true;
      })
      .map(o -> {
          try {
              return process(o);
          } catch (Exception e) {
              log.error("failed", e);
              return null;
          }
      })
      .filter(Objects::nonNull)
      ...
Java

ここまで来ると、
「for 文で書いてくれ…」と心の底から思われます。


なぜ「使い過ぎ」が問題になるのか

読み手の負荷が爆増する

モダン構文は、
“慣れた人が読む前提” の表現 です。

チーム全員がラムダ・Stream・Optional に慣れていればまだいいですが、
そうでない場合、
「一部の人しか読めないコード」が量産されます。

それは、
チーム開発としては失敗 です。

バグが見つけにくくなる

複雑な Stream チェーンやネストした Optional は、
デバッガで追いにくく、
ログも仕込みにくく、
バグの原因箇所を特定しづらくなります。

「一行で書ける」ことと
「一行で理解できる」ことは別物です。


実務的な指針:「モダン構文の使い方」のバランス感覚

まずは「読みやすさ」を最優先にする

モダン構文を使うかどうかの基準は、
「この書き方の方が読みやすいか?」
これだけでいいです。

「書けるから書く」ではなく、
「読んだときにスッと頭に入るか」で判断してください。

チームの平均レベルを基準にする

あなたがどれだけモダン構文に慣れていても、
チームの半分以上が「うっ…」となるなら、
それはやり過ぎです。

コードは「自分のため」ではなく、
「チーム全員のため」に書くもの です。

「まず素直に書いてから、必要ならモダンにする」

いきなり Stream や Optional で書き始めるのではなく、
一度、素直な for 文や if で書いてみてください。

そのうえで、
「ここは Stream にした方が読みやすいな」
「ここは Optional の方が意図が伝わるな」
と思ったところだけ、モダン構文に置き換える。

この順番を守るだけで、
「モダン構文の使い過ぎ問題」はかなり防げます。


初心者向けの実践ルール

  • ルール 1:Stream は「シンプルな変換・集計」にだけ使う
  • ルール 2:Optional は「戻り値」にだけ使う(最初は)
  • ルール 3:複雑なビジネスロジックは素直な if / for で書く
  • ルール 4:一度 for 文で書いてから、必要ならモダン構文にする
  • ルール 5:自分より“Java が得意でない人”が読んでも理解できるかを想像する

この 5 つを意識するだけで、
「モダン構文を知っているけど、ちゃんと節度を持って使える人」になれます。


まとめ:モダン構文の使い過ぎ問題を自分の言葉で説明するなら

あなたの言葉で整理すると、こうなります。

「モダン構文の使い過ぎ問題とは、
ラムダ・Stream・Optional などを“使えるから”といって盛り込みすぎて、
結果としてコードが読みにくくなり、
チーム全体の理解・保守・デバッグが難しくなること。

モダン構文は目的ではなく手段であり、
“読みやすさ” と “チームの平均レベル” を基準に、
使う場所と使わない場所を選ぶことが大事。
まず素直に書き、必要なところだけモダンにする、
という順番を守ることで、バランスの良いコードになる。」

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