変数の有効範囲
変数は、メソッドの中だけでなく、メソッドの外でも宣言することができます。 変数が利用できる範囲をその変数の有効範囲(scope)といいます。 ここでは、いろいろな変数の有効範囲について学習しましょう。
ローカル変数
これまでの学習で使われてきた、メソッドの中で宣言される変数は、ローカル変数(local variable)と呼ばれます。 for文で宣言された変数もローカル変数です。 ローカル変数は、それを宣言したブロックの中やfor文の中でのみ有効となります。 メソッドの仮引数もローカル変数と同じ扱いとなり、メソッドの中でのみ有効です。
メソッドの戻り値のところで登場した、sumUpメソッドをもう一度見てください。 初項mから末項nまでのすべての整数の和を求めるメソッドでしたね。
MethodSample04.java(再掲)
class MethodSample04 { static int sumUp(int m, int n){ int sum = 0; for(int i = m; i <= n; i++){ sum += i; } return sum; } public static void main(String[] args) { int s; s = sumUp(1, 10); System.out.println("1から10までの和は" + s + "です。"); s = sumUp(-2, 4); System.out.println("-2から4までの和は" + s + "です。"); s = sumUp(-5, 2); System.out.println("-5から2までの和は" + s + "です。"); } }
MethodSample04のsumUpメソッドはfor文を利用していましたが、これをwhile文で書き換えてみましょう。 次のScopeSample01は、while文を利用するsumUpメソッドのサンプルプログラムです。 MethodSample04のsumUpメソッドでは、変数iをインクリメントしていましたが、ScopeSample01のsumUpメソッドでは、仮引数mをインクリメントしている点に注意してください。 また、ScopeSample01では、初項と末項はキーボードから入力します。
ScopeSample01.java
import java.io.*; class ScopeSample01 { static int sumUp(int m, int n){ //仮引数m, nの有効範囲はここから int sum = 0; //ローカル変数sumの有効範囲はここから while(m <= n){ sum += m; m++; } System.out.println("[1] m = " + m); return sum; } //仮引数m, n, ローカル変数sumの有効範囲はここまで public static void main(String[] args) throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); System.out.print("初項m : "); int m = Integer.parseInt(br.readLine()); //ローカル変数mの有効範囲はここから System.out.print("末項n : "); int n = Integer.parseInt(br.readLine()); //ローカル変数nの有効範囲はここから int sum = sumUp(m, n); //ローカル変数sumの有効範囲はここから System.out.println("[2] m = " + m); System.out.println(m + "から" + n + "までの和は" + sum + "です。"); } //ローカル変数m, n, sumの有効範囲はここまで }
ScopeSample01の実行結果
初項m : 1 末項n : 10 [1] m = 11 [2] m = 1 1から10までの和は55です。
ローカル変数の有効範囲は、それが宣言された直後から、宣言を含むブロックの最後までです。 従って、sumUpメソッドの中で宣言されているローカル変数sumの有効範囲は、宣言された直後から、sumUpメソッド本体の最後までとなります。 同様に、mainメソッドの中で宣言されているローカル変数sumの有効範囲は、宣言された直後から、mainメソッド本体の最後までとなります。 このため、それぞれのメソッドの中で宣言されている変数sumは、名前は同じでも、実際には別の変数ということになります。 このように、異なるメソッドでは、同じ名前のローカル変数を宣言して使うことができます。 このおかげで、メソッド毎に変数名をsum1、 sum2のように使い分ける必要は無いのです。
メソッドの仮引数もローカル変数と同じ扱いになります。 仮引数の有効範囲は、メソッド本体の先頭から最後までです。 sumUpメソッドの仮引数m, nとmainメソッドのローカル変数m, nは、名前はたまたま同じですが、やはり別の変数なのです。 ScopeSample01の実行結果からも、そのことがお分かりいただけるでしょう。 ここでは、sumUpメソッドの仮引数mの値は、1から10までインクリメントされていき、その値が順に変数sumに加算されていきます。 そして、while文を終了した時点では、mの値は11となっています。 一方で、mainメソッドのローカル変数mの値は、sumUpメソッド呼び出し後も1のままとなっています。
ローカル変数の有効範囲は、それが宣言された直後から、宣言を含むブロックの最後までである。
異なるメソッドでは、同じ名前のローカル変数を宣言することができる。
メソッドの仮引数の有効範囲が、そのメソッド本体に限られることから、複数のメソッドで同じ名前の仮引数を使うこともできます。 ScopeSample02では、maxメソッドとminメソッドが宣言されています。 それぞれ2数の最大値と最小値を求めるメソッドです。
ScopeSample02.java
import java.io.*; class ScopeSample02 { static int max(int a, int b){ return a > b ? a : b; } static int min(int a, int b){ return a < b ? a : b; } public static void main(String[] args) throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); System.out.print("整数a : "); int a = Integer.parseInt(br.readLine()); System.out.print("整数b : "); int b = Integer.parseInt(br.readLine()); System.out.println(a + ", " + b + "の最大値は" + max(a, b) + "です。"); System.out.println(a + ", " + b + "の最小値は" + min(a, b) + "です。"); } }
ScopeSample02の実行結果
整数a : 5 整数b : 7 5, 7の最大値は7です。 5, 7の最小値は5です。
2つのメソッドmaxとminの仮引数a, bは、名前は同じでも別の変数です。
今回は、maxメソッドでは、a > b ? a : b、minメソッドでは、a < b ? a : bという式の値を直接戻り値として返しています。
異なるメソッドでは、同じ名前の仮引数を宣言することができる。
フィールド
メソッドの中で宣言されるローカル変数に対して、メソッドの外で宣言される変数をフィールド(Field)と呼びます。 フィールドの有効範囲はクラス全体です。 従って、フィールドは複数のメソッドで共有できる変数と言えます。
フィールドの宣言は次のような形になります。
構文 : フィールドの宣言
static 型名 フィールド名;
フィールドには、staticの付くものと付かないものがありますが、staticの付いたメソッドの中でアクセスされるフィールドは、staticを付けて宣言する必要があります。 staticが付くフィールドをクラス変数(class variable)、付かないフィールドをインスタンス変数(instance variable)と呼びます。 staticの意味については後ほど学習しましょう。
フィールド名は識別子(第4章)の規則に従ってつけます。 例えば、int型のフィールドhiScoreは、次のように宣言します。
static int hiScore;
それでは、サンプルプログラムを見てみましょう。 ScopeSample03は、簡単な魚釣りゲームです。 と言っても、プレイヤーがすることは、実際に釣るかどうかを決めることだけです。 魚の重さは乱数で決定されます。 次々に魚を釣り、魚の重さがそれまでの最高記録なら、ハイスコアとして保存されます。
ScopeSample03.java
import java.io.*; import java.util.Random; class ScopeSample03 { static int hiScore; //フィールド static void updateHiScore(int score){ if(score > hiScore){ System.out.println("ハイスコア!"); hiScore = score; } } public static void main(String[] args) throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); Random rnd =new Random(); while(true){ int cont; do{ System.out.print("釣りますか。[Yes…1/No…0]:"); cont = Integer.parseInt(br.readLine()); }while(cont !=1 && cont != 0); if(cont == 0) break; int score = rnd.nextInt(1000); System.out.println(score + " グラムの魚が釣れました。"); updateHiScore(score); System.out.println("現在のハイスコアは " + hiScore + " グラムです。"); System.out.println(); } } }
ScopeSample03の実行結果
釣りますか。[Yes…1/No…0]:1 647 グラムの魚が釣れました。 ハイスコア! 現在のハイスコアは 647 グラムです。 釣りますか。[Yes…1/No…0]:1 243 グラムの魚が釣れました。 現在のハイスコアは 647 グラムです。 釣りますか。[Yes…1/No…0]:1 946 グラムの魚が釣れました。 ハイスコア! 現在のハイスコアは 946 グラムです。 釣りますか。[Yes…1/No…0]:0
updateHiScoreメソッドは、魚の重さを仮引数として受け取り、その値がハイスコアより高いならば、その重さを新しいハイスコアとして保存します。
また、mainメソッドでは、次のようにハイスコアを表示しています。
System.out.println("現在のハイスコアは " + hiScore + " グラムです。");
updateHiScoreメソッドとmainメソッドのどちらからも、確かにhiScoreにアクセスできていますね。 2つののメソッドに記述されているhiScoreフィールドは、名前が同じなだけではなく、本当に同一の変数を表しています。
フィールドの有効範囲は、クラス全体である。