変数の有効範囲

変数は、メソッドの中だけでなく、メソッドの外でも宣言することができます。 変数が利用できる範囲をその変数の有効範囲(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フィールドは、名前が同じなだけではなく、本当に同一の変数を表しています。

フィールドの有効範囲は、クラス全体である。