0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Effective Java 9.57: ローカル変数のスコープを最小限にする

0
Posted at

9.57: ローカル変数のスコープを最小限にする

結論

ローカル変数(メソッド内の変数)のスコープは可能な限り狭くするべき。
理由は可読性・保守性の向上、バグ(未初期化・誤再利用)の低減、リソースやオブジェクトのライフタイムを短くして意図しない参照を減らすため。
try-with-resources・早期 return・小さなヘルパーメソッドを使うと自然に狭められる。


良い例

変数を出現箇所の直前で宣言し、責務を分離してスコープを最小化した例。

public void processFile(Path path) throws IOException {
    // ファイル読込のスコープは try-with-resources 内に限定
    try (BufferedReader reader = Files.newBufferedReader(path)) {
        String line;
        while ((line = reader.readLine()) != null) {
            // 1行の処理に使う変数は for-loop 内に限定
            String normalized = normalize(line);
            if (shouldSkip(normalized)) continue;
            handle(normalized);
        }
    }
}

private String normalize(String s) {
    // 小さなヘルパーに分けることで、ローカル変数は必然的に狭くなる
    return s.trim().toLowerCase(Locale.ROOT);
}

ポイント:

  • reader の寿命は try ブロック内に限定される → リソース解放が明確
  • line / normalized は最小限のスコープ(while / loop 内)にある
  • 処理の単位をメソッドに分けると、それぞれのローカル変数がさらに縮まる

悪い例

変数をメソッド冒頭でまとめて宣言し、長いスコープで使う例(バグを招きやすい)。

public void badProcess(File file) throws IOException {
    BufferedReader reader = null;
    String line = null;
    String normalized = null;

    try {
        reader = new BufferedReader(new FileReader(file));
        while ((line = reader.readLine()) != null) {
            normalized = line.trim().toLowerCase();
            if (someCondition(normalized)) {
                // 何かの処理
            }
        }
        // ここで誤って normalized を使ってしまうミスが入りやすい
        if (normalized != null && normalized.contains("error")) {
            // ここは意図しないチェックになっている可能性がある
        }
    } finally {
        if (reader != null) reader.close();
    }
}

問題点:

  • 変数がメソッド全体に露出しているため、ループ外で誤って使う可能性が高い
  • reader を手動クローズしているため、例外時の処理が複雑で安全性が低い(try- with-resources を使えば解決)
  • 変数再利用により、バグの原因になりやすい(最後の normalized が想定外の値を参照している等)

なぜスコープを狭めるべきか

  • 可読性の向上:
    変数がどこで使われているかが一目でわかる。
    読んだ人が心配する範囲が小さい。
     
  • バグ防止:
    未初期化・誤用・誤再利用のリスクが減る(特にループ外での誤参照)。
     
  • リソース管理が明確になる:
    リソース(ストリーム、ソケット等)を必要最小限のブロックで保持でき、早期解放が容易。
     
  • GC に有利(場合がある):
    ローカル変数のスコープを短くすると、JVM がオブジェクトの参照を早く切る可能性があり、メモリ回収が促進される(JIT 最適化の影響もあるので必ずしも顕著ではないが原則として有用)。
     
  • スレッド安全性の誤解を防ぐ:
    ローカル変数は原則スレッドローカルだが、ラムダや匿名クラスにキャプチャされると寿命が延びる。
    狭いスコープはキャプチャのタイミングを制御しやすい。
     
  • テストしやすさ・リファクタの助け:
    小さな変数スコープはメソッドを小さく保つ助けになり、単体テストや理解が容易になる。

まとめ

  • 変数は使い始める直前で宣言する
  • ループ内でしか使わないものは必ずループ内で宣言する
  • リソースは try-with-resources を使ってスコープを限定する
  • 複雑な処理は小さなメソッドに分割し、各メソッドでのスコープを狭める
  • 変数を再利用する(再代入して別目的に使う)ことを避ける。名前も責務に応じて分ける
  • (ラムダ/匿名クラスで変数をキャプチャするときは、その変数のスコープがキャプチャのライフタイムと一致するか注意する)
  • (コードレビューで「この変数、もっと局所化できないか?」をチェック項目に入れる)
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?