こんにちは、decoponです。
今回は、Javaで再帰処理を書いていたら見事に落ちた StackOverflowError
について、
- 「スタックって何?🤔」
- 「なんで再帰で落ちるの?💥」
- 「どう書けば安全?🛡️」
という疑問に寄り添いながら、やらかしからの気づきをまとめてみました📘✨
🐾 はじめに ― 再帰で書いたら帰ってこなくなりました
「関数が自分を呼び出す?なんかかっこいいぞ…!」
そんな気持ちで再帰にチャレンジした私が書いたコード
public void callMe() {
callMe(); // ❌ 終了条件なし!永遠に呼び続けてしまう
}
実行した結果:
Exception in thread "main" java.lang.StackOverflowError
…落ちました。しっかりと落ちました。
🍱 StackOverflowErrorとは?
💡 スタックって何?
スタックとは、Javaがメソッド呼び出しの履歴を覚えておくための領域です。
-
最後に呼び出したものから順に処理される(LIFO構造)
-
呼び出しが終わるまでスタックに積み上げられ続ける
🎯 たとえるなら: 「重ねたお弁当箱」🍱 上から順にしか片付けられません!
⚠️ StackOverflowErrorの原因
- メソッドが止まらず呼び続けている
- スタックに積まれる回数が限界を超える
- 多くは「再帰処理」によるもの
public int count(int n) {
return count(n + 1); // ❌ 方向も逆!止まらない!
}
✅ エラーの特徴:
- 同じメソッドのスタックトレースが大量に並ぶ
- Error クラス → Exception と違ってcatchでは回避不可
- 原因は「終了条件なし」の再帰パターンが多い
💥 よくある再帰のやらかし例
🚪 終了条件がない
public void recurse() {
recurse(); // ❌ どこで止まる予定…?
}
→ ベースケース(出口)を書きましょう!
🔄 進み方が逆
public void countdown(int n) {
if (n == 0) return;
countdown(n + 1); // ❌ 終了条件からどんどん遠ざかってる!
}
→ n - 1
にするなど、終了に向かって進むよう修正!
🪄 相互再帰で迷子に
public void foo() {
bar(); // foo → bar
}
public void bar() {
foo(); // bar → foo → foo → bar → ...
}
→ お互いが呼び合って無限ループ…😨
🛠️ StackOverflowErrorの回避ポイント
✅ ベースケースを明示しよう
public int factorial(int n) {
if (n == 0) return 1; // ✅ 終了条件
return n * factorial(n - 1);
}
→ 呼び出しが終わることでスタックが順に片付いていきます✨
🔁 ループで書けるならループもアリ!
for (int i = n; i > 0; i--) {
System.out.println(i); // 👌 スタックを使わず安全!
}
→ 処理が重い・回数が多いときは、ループの方が安心です🧘♂️
🗒 設計の気づき
StackOverflowErrorはただの落下事故じゃなくて、 「コードに体力ってあるんだな」 「帰ってこられる設計、大事だな」 って気づかせてくれるエラーでした。
- 呼び出しと戻りを意識したコード設計
- 再帰でなくてもできることはループでもOK
- 呼びすぎる前に「戻れるかな?」を考える癖
📩 最後に
もしこの記事が、 再帰でつまずいた誰かの「なんでだろう…」にそっと寄り添えていたら嬉しいです🐾
「ただいま」って、帰ってこられないと言えないのです。🧸 by moco
StackOverflowErrorは、 “設計にやさしさを持てていますか?”と静かに教えてくれるエラー。 これからも、一緒にやさしくコードを書いていきましょう ☕✨