ブロックとスコープ

ブロック

ブロックとは、{} で括られた、 プログラム処理上のひとまとまりのことである。

ブロックの例1:
if( a > 0) {               //1行目 ブロック開始
    System.out.println(a); //2行目
}                          //3行目 ブロック終了
上記で、1行目の { でブロックが開始し、3行目の } でブロックが終了する。 2行目、ブロック内の処理となる。

ブロックの例2:

while( a > 0) {            //1行目 ブロック開始
    a--;                   //2行目
    System.out.println(a); //3行目
}                          //4行目 ブロック終了
上記で、1行目の { でブロックが開始し、4行目の } でブロックが終了する。 2行目、3行目は、ブロック内の処理となる。

ブロックを使う理由は2つある。
  1. if, for, while等の分岐処理で複数の処理を実行したいとき。
    if, for, while等の分岐処理は、一つの処理のみを実行する場合、 文法上はブロックを利用する必要はない。
    ブロックを利用しない例:
    if( a > 0) System.out.println(a);
    
    しかし、分岐処理で複数の処理(行)を実行したい場合、ブロックを利用する必要がある。 ブロックを利用することにより、ブロック処理を一つの処理のまとまりとして扱うことができる。
    ブロックを利用する例:
    if( a > 0) {
        System.out.print("A の値は"); //複数の処理をtrue の時に実行したい
        System.out.println(a);        //複数の処理をtrue の時に実行したい
    }
    
  2. ブロック内のスコープで有効なローカル変数を利用するとき
    詳細は下記スコープの説明を参照
ブロックの利用の注意事項

スコープ

変数にはスコープ(変数にアクセスできるプログラムの範囲)がある。 例えば、 局所変数(ローカル変数)のスコープは宣言された場所から宣言されたブロックの終りまでである。

スコープの例1 (エラーの例)
public class Foo {
    public static void main (String[] args){
        int a=0; // <-- ここで宣言された変数は、mainメソッドの終りまで参照可能

        while(a < 10){
            int b; // <-- ここで宣言された変数は、whileブロックの終りまで参照可能
            b = a;
            a++;
        } // <-- whileブロック終り。(b のスコープ終り)
        System.out.println(a); // <-- a は参照できる
        System.out.println(b); // <-- b はwhileブロックの外なので参照できない
    } // <-- ここでmainメソッド(a のスコープ)終り 
}

スコープの例2 (エラーの例)
public class Bar {
    public static void main (String[] args){
        int a=0; // <-- ここで宣言された変数は、mainメソッドの終りまで参照可能

        for(int b=0; b<10; b++){ //forの初期化文で宣言された変数は、forブロックの終りまで参照可能
            a = b;
        } // <-- forブロック終り。(b のスコープ終り)
        System.out.println(a); // <-- a は参照できる
        System.out.println(b); // <-- b はforブロックの外なので参照できない
    } // <-- ここでmainメソッド(a のスコープ)終り 
}

スコープの例3 (エラーの例)
public class FooBar {
    public static void main (String[] args){

        for(int b; b<10; b++){ //forの初期化文で宣言された変数は、forブロックの終りまで参照可能

            if(b%2 == 0){
                int c;  // <-- ここで宣言された変数はifブロックの終りまで参照可能
                c = 1; // <-- c はifブロックの中なので参照可能
            }else{
                c = 0; // <-- c はifブロックの外なので参照できない
            }

            System.out.println(b); // <-- b はforブロックの中のため参照可能。
            System.out.println(c); // <-- c はifブロックの外のため参照できない。
        } // <-- forブロック終り。(b のスコープ終り)
    }
}

練習課題

以下のプログラムでは、forループを最後に抜ける前の i の状態だけを表示したい。 (最後にループした時のiの値)
以下の例では、スコープがforループの外にある変数 last を準備し、ループ中の変数 i の値を保持して表示したい。
しかし、以下のソースコードではコンパイル時にエラーが出る。
public class Scope2 {
    public static void main(String[] args){
        int last;
        for(int i = 0; i<10; i++){
            last = i;
        }
        System.out.println("last の最後の値は "+ last); // <-- ここでコンパイルエラー
    }
}
「変数 last は初期化されていない可能性があります。」とコンパイルエラーが出る。
これは、last に値が代入される前に参照される可能性があるためである。
for文の中で last = i; で 変数last に代入しているが、 for文のブロックはfor文の条件が true の時にしか実行されないので、 必ず値が代入されるかコンパイラが判断できない。 以下の出力結果のように、lastの値を表示するように、last の初期化を追加せよ。
※ヒント: 必ず last に値が代入されるように、最初に変数 last に値を代入しておく。
$ java Scope2
last の最後の値は 9
$

mackin