Java | 基礎文法:変数ウォッチ

Java Java
スポンサーリンク

変数ウォッチの全体像

変数ウォッチは、プログラムを一時停止して「いまこの瞬間の変数や式の値」を安全に観測するデバッグ技法です。IDE(IntelliJ IDEA、Eclipse、VS Code など)のブレークポイントで実行を止め、ウォッチリストに変数や式を登録すると、行ごとに値の変化を追えます。思い込みではなく「事実の値」を見ることで、ロジックの誤りや境界条件の取り違えを素早く特定できます。


目的と効果の理解

何のためにウォッチするか

  • 状態確認: 変数・フィールド・戻り値が「期待通りか」をその場で検証する。
  • 変化の追跡: ステップ操作で前後の差分を見て、どこでズレたかを特定する。
  • 仮説検証: ウォッチ式にロジック断片を入れ、意図どおりに評価されるか試す。

ログ出力との使い分け

  • ウォッチの利点: コードを汚さずに即座に観測でき、副作用なしで安全。
  • ログの利点: 本番での再現・履歴の蓄積に強い。
  • 指針: 開発時はウォッチで原因を狭め、確定後にログへ落とすと効率的。

基本操作とセットアップ

ブレークポイントで止める

  • 配置: 問題が起きる直前の行にブレークポイントを置く。
  • ステップ: ステップイン/オーバー/アウトで流れを追いながら値を見る。
  • 条件付き: 「id == ‘X-999’ のときだけ止める」など条件を付けると、大量データでもピンポイントで観測できる。

ウォッチリストに登録する

  • 変数: total, user.name, list.size() のような参照・プロパティ。
  • 式: price * (1 + rate), Objects.equals(a, b), map.get(key) などの評価式。
  • 評価: IDEの「Evaluate Expression」でその場のスコープを使って即時評価できる。

重要ポイントの深掘り

副作用のない観測を徹底する

  • 安全な式だけを評価: get() が重い I/O を伴う、next() が状態を進める、remove() が変更する——この類はウォッチで呼ばない。
  • toString の落とし穴: 一部の toString() が内部アクセスや計算を行う場合がある。巨大コレクションの toString() は避け、サイズと代表要素のみを見る。

コレクション・マップの中身を見るコツ

  • サイズと要約: list.size(), set.contains(x), map.containsKey(k) で輪郭を掴む。
  • 代表要素: list.get(i) を数点ウォッチ、map.get(k) で箇所を絞る。
  • 反復中の注意: 反復を進めるメソッド(iterator.next())はウォッチで呼ばない。現在の要素はループ変数を直接ウォッチ。

条件付きブレークポイントとヒットカウント

  • 条件付き: user != null && user.isActive() などで「特定条件のみ停止」。
  • ヒットカウント: 「100回目で止める」など、境界付近の観測に有効。
  • フィルタ: スレッド名やクラス単位で止める設定を使うと、並行環境でも狙い撃ちできる。

スレッド・同期の観測

  • スレッド別に見る: 現在のスレッド名/IDをウォッチし、意図したスレッドで動作しているか確認。
  • ロック中の観測: 共有状態はロック取得中にウォッチ。非同期更新中は値が揺れるため注意。

実例で身につける

例 1: 計算ロジックのズレを特定する

public class PriceCalc {
    static int taxed(int subtotal, double rate) {
        int tax = (int) Math.round(subtotal * rate);
        return subtotal + tax;
    }
    public static void main(String[] args) {
        int subtotal = 999;
        double rate = 0.1;
        int total = taxed(subtotal, rate);
        System.out.println(total);
    }
}
Java
  • ウォッチする式: subtotal, rate, subtotal * rate, Math.round(subtotal * rate), tax, subtotal + tax
  • 狙い: 小数点の丸め位置が正しいか(合計丸めか税額丸めか)を、行ごとに値で確認する。

例 2: ループ中の境界条件を目視で潰す

public class Traverse {
    public static void main(String[] args) {
        int[] a = {10, 20, 30};
        for (int i = 0; i <= a.length; i++) { // バグ:<=
            System.out.println(a[i]);
        }
    }
}
Java
  • ブレークポイント: System.out.println(a[i]) に設定。
  • ウォッチ: i, a.length, i < a.length の真偽。
  • 結果: i == a.length のときに範囲外(IOBE)が起きる事実を確認し、< へ修正。

例 3: マップ照合の漏れを特定する

import java.util.*;

public class Match {
    public static void main(String[] args) {
        var ids = List.of("A", "B", "C");
        var map = Map.of("A", 1, "C", 3);
        for (String id : ids) {
            Integer v = map.get(id);
            if (v != null && v > 0) {
                System.out.println(id + ":" + v);
            }
        }
    }
}
Java
  • ウォッチ: id, map.containsKey(id), map.get(id), v != null の判定。
  • 気づき: 「B はキーがない」事実が即座に分かる。必要なら containsKeyget の使い分けを見直す。

例 4: 条件付きブレークポイントで特定IDだけ止める

for (String id : ids) {
    process(id); // 条件:id.equals("X-999") のときだけ停止
}
Java
  • ウォッチ: 問題の id でのみ停止し、process 内の statecounter を観測。大量データでも効率よく原因を絞れる。

実戦の注意点とベストプラクティス

評価による副作用を避ける

  • 原則: ウォッチは参照・算術・比較など「読み取りのみ」に限定。
  • 避ける: I/O、ネットワーク、DB 呼び出し、イテレータ前進、ミューテーション(追加・削除)。

観測点を絞る

  • 効果的なポイント: 条件分岐の直前、ループの境界、メソッドの入口と出口。
  • 少数精鋭: ウォッチ式は「なぜ」を説明できる最小セットに絞る。増やしすぎるとノイズになる。

ウォッチで確信、修正はコードへ

  • 型へ落とす: たとえば「null だった」事実を Objects.requireNonNull や早期リターンのガードへ反映。
  • テストに昇華: 観測した境界条件を単体テストにし、再発防止をコード化する。

仕上げのアドバイス(重要部分のまとめ)

変数ウォッチは「いま、ここ」の値を安全に観測して、誤りを事実で突き止めるための最短ルートです。ブレークポイントで止め、ウォッチ式で仮説を検証し、ステップ操作で変化を追う。評価は副作用ゼロに徹し、巨大構造は要約(サイズ・代表要素)で見る。条件付きブレークポイント・ヒットカウントを活用し、並行環境ではスレッドと同期の文脈も観測する。ウォッチで得た事実は、ガード節・修正・テストへ速やかに落とし込む——この型が身につけば、デバッグは短く、正確になります。

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