こんにちは、かつコーチです。
「if文の中で作った変数を、外側で使おうとしたらエラーになった」という経験はありませんか。
これはスコープ(変数が有効な範囲)を理解していないとつまずきやすいポイントです。
この記事では、スコープの基本ルールと、変数の生存期間について解説します。
スコープとは
変数が有効な範囲
スコープとは、変数を参照できる範囲のことです。
Javaでは、変数は{ }(波括弧)で囲まれたブロックの中で宣言され、そのブロックの中でだけ有効になります。
public class Sample {
public static void main(String[] args) {
int x = 10; // このブロック(mainメソッド)の中で有効
if (x > 5) {
int y = 20; // このifブロックの中でだけ有効
System.out.println(x + y); // 30(xもyも参照できる)
}
System.out.println(x); // 10(xは参照できる)
// System.out.println(y); ← コンパイルエラー:yはここでは無効
}
}
yはifブロックの中で宣言されているため、そのブロックを抜けた時点で参照できなくなります。
なぜスコープが必要なのか
スコープがあることで、変数の影響範囲を必要最小限に限定できます。
もしスコープの概念がなければ、プログラムのどこか一箇所で変数名を変更すると、意図せず遠く離れた別の処理に影響が及ぶ可能性があります。
スコープを狭く保つことは、「その変数がどこで使われているか」を追いやすくし、バグの原因調査を容易にするというメリットにもつながります。
基本の書き方・実装手順
手順1:メソッドのスコープ
メソッド内で宣言した変数(ローカル変数)は、そのメソッドの中でだけ有効です。
public class Sample {
public void methodA() {
int value = 1;
System.out.println(value);
}
public void methodB() {
// System.out.println(value); ← コンパイルエラー:methodAのvalueは見えない
}
}
methodAのvalueと、仮にmethodBで同名のvalueを宣言しても、それぞれ完全に別の変数として扱われます。
手順2:ブロックのスコープ(if文・for文)
if文やfor文などの制御構文も、それぞれ独自のスコープを作ります。
public class Sample {
public static void main(String[] args) {
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
// System.out.println(i); ← コンパイルエラー:forの外ではiは無効
}
}
for文のカウンタ変数iは、forブロックの中だけで有効なスコープを持ちます。
手順3:フィールド(インスタンス変数)のスコープ
クラス直下で宣言した変数(フィールド)は、そのクラス内のどのメソッドからでもアクセスできます。
public class Counter {
int count = 0; // フィールド:クラス全体で有効
public void increment() {
count++; // どのメソッドからでもアクセス可能
}
public void show() {
System.out.println(count);
}
}
フィールドのスコープは、ローカル変数よりも広い「クラス全体」になります。
つまずきやすい設定・注意点:変数の生存期間との関係
スコープとよく混同されるのが生存期間(変数がメモリ上に存在している期間)です。
ローカル変数は、そのメソッドの呼び出しが終わると同時にメモリから消えます。
一方、フィールドはインスタンスが存在する限りメモリ上に残り続けます。
つまり、スコープは「どこから参照できるか」、生存期間は「いつまでメモリに存在するか」という、似ているけれど別の概念です。
よくあるつまずきポイント・エラー対処
ブロックの外で変数を使おうとしてエラーになる
初心者が最初につまずきやすいのが、条件分岐の中で宣言した変数を外で使おうとするケースです。
❌Before
public class Sample {
public static void main(String[] args) {
int score = 75;
if (score >= 60) {
String result = "合格";
} else {
String result = "不合格";
}
System.out.println(result); // コンパイルエラー
}
}
error: cannot find symbol
symbol: variable result
私が実際にこのエラーに遭遇したのは、if/elseそれぞれの中で結果を保持する変数を作り、最後にまとめて出力しようとしたときでした。resultはifブロックとelseブロックそれぞれのスコープの中でしか有効ではないため、ブロックの外にあるprintlnからは見えません。
✅After
ブロックの外側で変数を先に宣言し、ブロックの中で値を代入する形にすれば解決します。
public class Sample {
public static void main(String[] args) {
int score = 75;
String result; // 外側のスコープで宣言
if (score >= 60) {
result = "合格";
} else {
result = "不合格";
}
System.out.println(result); // 合格
}
}
ifとelseの両方で必ず代入されることが保証されていれば、宣言時に初期値を入れなくてもコンパイルエラーにはなりません。
同じスコープ内で変数名が重複する
同じスコープの中で同名の変数を二重に宣言すると、コンパイルエラーになります。
public void method() {
int value = 1;
int value = 2; // コンパイルエラー:variable value is already defined
}
内側のブロックで外側と同じ変数名を使うことも、Javaでは許可されていません(言語によっては許可される場合がありますが、Javaは禁止しています)。
int value = 1;
if (true) {
int value = 2; // コンパイルエラー:外側のvalueと重複
}
応用・一歩先の使い方
スコープを意識した設計の勘所
実務では、「変数のスコープはできるだけ狭くする」のが良いコードの原則の1つとされています。
フィールドとして広いスコープを持たせるべきか、メソッド内のローカル変数として狭く保つべきかを都度検討することで、後から見返したときに「この変数がどこまで影響するか」が把握しやすくなります。
まとめ
この記事のポイント
- スコープとは変数が有効な範囲のことで、
{ }のブロック単位で区切られる - ローカル変数はそのメソッド・ブロックの中でだけ有効
- フィールドはクラス全体で有効になり、ローカル変数よりスコープが広い
- スコープ(参照できる範囲)と生存期間(メモリに存在する期間)は似て非なる概念
次に読むべき記事
- クラスの構成要素であるフィールドについては、次章のクラスとオブジェクトの基本の記事で詳しく解説します
- コマンドライン引数の受け取り方については、次の記事で解説します
タグ: Java, 初心者向け, 基本文法