Code Fix

中級

値が変わらないときに疑う変数の隠蔽

同名のローカル変数がフィールドを覆い隠します。コンストラクタでthis.が必要な理由を扱います。

なぜエラーが出ないのか

出力: 10
(エラーなし)
javacは何も報告しません。文法として正しいためです
出力: 10
実際の挙動 — 期待した結果と食い違っている箇所
見つけ方
エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます

このエラーが出る典型パターン

パターン1

 1  public class Main {
 2      static int count = 10;
 3  
 4      static void reset() {
 5          int       count = 0;
                 ^
 6      }
 7      public static void main(String[] args) {
 8          reset();
 9          System.out.println(count);
10      }
11  }
出力: 10期待: 0 (コンパイルは通っています)

型を書くとローカル変数が新しく作られ、フィールドは変わりません。これを変数の隠蔽と呼びます。

直し方: int を (空) にします。

この問題を解いてみる →

広告
広告スロット(未設定)

パターン2

 1  public class Main {
 2      int value;
 3  
 4      Main(int value) {
 5                value = value;
               ^
 6      }
 7      public static void main(String[] args) {
 8          System.out.println(new Main(5).value);
 9      }
10  }
出力: 0期待: 5 (コンパイルは通っています)

引数とフィールドが同名のとき、引数が優先されます。フィールドを指すにはthis.が要ります。

直し方: (空) を this. にします。

この問題を解いてみる →

パターン3

 1  public class Main {
 2      public static void main(String[] args) {
 3          int sum = 0;
 4          for (int i = 0; i < 3; i++) {
 5              int     sum = sum + i;
                    ^
 6          }
 7          System.out.println(sum);
 8      }
 9  }
Main.java:5: error: variable sum is already defined in method main(String[])

ループの中で同名を再宣言すると、外側と衝突します。集計用の変数はループの外で1回だけ宣言します。

直し方: int を (空) にします。

この問題を解いてみる →

パターン4

 1  public class Main {
 2      int total = 100;
 3      void addAll(int[] nums) {
 4          int       total = 0;
                 ^
 5          for (int n : nums) total += n;
 6      }
 7      public static void main(String[] args) {
 8          Main m = new Main();
 9          m.addAll(new int[]{1, 2, 3});
10          System.out.println(m.total);
11      }
12  }
出力: 100期待: 6 (コンパイルは通っています)

型を書くとローカル変数total が新しく作られ、フィールドのtotalは別物のまま変わりません。

直し方: int を (空) にします。

この問題を解いてみる →

パターン5

 1  public class Main {
 2      static int square(int n) {
 3          int        n = n * n;
                 ^
 4          return n;
 5      }
 6      public static void main(String[] args) {
 7          System.out.println(square(4));
 8      }
 9  }
Main.java:3: error: variable n is already defined in method square(int)

引数nはすでにその名前で宣言済みです。値を更新するだけなら型を書かずに代入します。

直し方: int を (空) にします。

この問題を解いてみる →

よくある誤解

エラーも警告も出ません。「値が変わらない」という症状だけが残るので、原因にたどり着きにくい種類のバグです。

実務での勘所

コンストラクタの引数にフィールドと同じ名前を使うこと自体は、実務でもよく使われる意図的な書き方です(むしろ推奨されることが多いスタイルです)。問題になるのは、意図的に同じ名前にしたのにthis.を付け忘れて代入文を書いてしまう場合だけです。javac自体はこのミスを警告しませんが、IntelliJ IDEAやEclipseの静的解析は「フィールドを隠蔽している」という警告を出すことが多いので、IDEの警告表示をオフにしていないか確認する価値があります。

演習をはじめる

関連するエラー

広告
広告スロット(未設定)