目次| |



16. 確実な代入

個々の局所変数は,その値にアクセスが発生するときには,確実に代入された(definitely assigned) 値をもたなければならない。 値へのアクセスは,単純な代入演算子 = の左辺にある場合を除く,式の中の任意の場所に現れる変数の単純名から構成する。

Javaコンパイラは,すべての局所変数のアクセスに対して,その局所変数がアクセス前に確実に代入されていることを保証するために,特定の慎重なフロー解析を行わなければならない。 確実に代入されていない場合は,コンパイル時エラーとしなければならない。

ここでは,用語“前で確実に代入されている”について厳密に規定する。 確実な代入とは,その局所変数へのアクセスを含む,コンストラクタ,メソッド,又は静的初期化子の先頭からそのアクセスまでに通過し得るすべての実行経路で,その局所変数が代入されていなければならないことを示す。 解析では,文及び式の構造を考慮する。さらに,式演算子!&&|| 及び ?:booleanオペランドを伴う演算子 &|^== 及び != ,並びに論理定数式に対しては,特別な処理を行う。 例えば,次のコードでは,Javaコンパイラは,メソッド呼出しの引数としてk がアクセスされる前に,k が確実に代入されていると判定する。


{
        int k;
        if (v > 0 && (k = System.in.read()) >= 0)
                System.out.println(k);
}

その理由は,そのアクセスは,次に示す式の値が真の場合にだけ発生し,その式の値が true になるのは, k への代入が実行された(正確には,評価された) 場合だけであることによる。

v > 0 && (k = System.in.read()) >= 0

同様に,次のコードでは,Javaコンパイラは,変数 kが そのwhile 文で確実に代入されていると判定する。


{
        int k;
        while (true) {
                k = n;
                if (k >= 5) break;
                n = 6;
        }
        System.out.println(k);
}

その理由は,while 文の条件式 trueの値が false になることはないので,そのwhile 文は, break文によってだけ終了し,k は, break 文の前で確実に代入されていることによる。

フロー解析では,特定の論理演算子と論理定数式の特別な場合を除いて,式の値は,考慮しない。 例えば,Javaコンパイラは,次のコードを,必ずコンパイル時エラーにしなければならない。


{
        int k;
        int n = 5;
        if (n > 2)
                k = 3;
        System.out.println(k);    // k is not "definitely assigned" before this
}

たとえ,n の値がコンパイル時に分かり,原理的には,k の代入が常に実行される (正確には,評価される) ことが既知であっても,エラーにしなければならない。 Javaコンパイラは,必ずここで規定する規則に従って動作しなければならない。 この規則では,定数式だけを判定する。 上記の例では,式 n > 2 は,15.27の定義による定数式ではない。

次に,別の例を挙げる。 Javaコンパイラは,k の確実な代入に関する限り,次のコードを受け付ける。


void flow(boolean flag) {
        int k;
        if (flag)
                k = 3;
        else
                k = 4;
        System.out.println(k);
}

その理由は,ここで説明している規則は,k は,flagが true であるか false であるかによらず必ず代入されていると判断してもよいことを規定していることによる。 しかし,この規則は,次のコードは,受け付けない。


void flow(boolean flag) {
        int k;
        if (flag)
                k = 3;
        if (!flag)
                k = 4;
        System.out.println(k);    // k is not "definitely assigned" before here
}

したがって,このコードをコンパイルすると,必ずコンパイル時エラーが発生しなければならない。

確実な代入のすべての場合を正確に規定するために,ここで次の二つの用語を定義する。

論理型値の式を規定するために,後者をさらに二つの場合に分ける。

ここで,真の場合 及び 偽の場合 とは,その式の値を指す。 例えば,次のコードでは,局所変数 k は,式が true の場合,式の評価の後で値が確実に代入されているが,式が false の場合は,代入されていない。

a && ((k=m) > 5)

その理由は,a偽の場合には,k への代入が実行されない(正確には,評価されない)ことによる。

“局所変数 V が,文又は式 X の後で確実に代入されている”とは,“V は,X が正常終了する場合,X の後で確実に代入されている”ことを意味する。 X が中途完了する場合,代入は,行われていないことがあってもよく,ここに述べた規則では,このことを考慮している。 この定義では,“V は, break; の後で確実に代入されている”という奇妙な文が常に成立する。 その理由は,break 文は,正常完了することがないので,break 文が正常完了する場合に V に値が代入されているという定義は,意味をなさないものとして成立する。

V を局所変数,abc,及び e を式,S 及び T を文とする。

16.1 確実な代入及び 式

16.1.1 論理定数式

V は,値が true である定数式が偽の場合,定数式の後で確実に代入されている。 V は,値が false である定数式が真の場合,定数式の後で確実に代入されている。

値が true である定数式が false になることはなく,値が false である定数式が true になることはないので,上記の定義は,意味をなさないものとして成立する。 この二つの定義は,論理演算子 &&|| 及び ! (16.1.316.1.416.1.5)を含む式の解析において役立つ。

16.1.2 論理値式

あらゆる論理式に対して,

16.1.3 論理演算子 &&

16.1.4 論理演算子 ||

16.1.5 論理演算子 !

16.1.6 論理演算子 &

16.1.7 論理演算子 |

16.1.8 論理演算子 ^

16.1.9 論理演算子 ==

16.1.10 論理演算子 !=

a != b に対する規則は,a^ b に対する規則 (16.1.8)と同じとする。

16.1.11 論理演算子 ? :

b 及び c を論理式と仮定する。

16.1.12 条件演算子 ? :

b 及び c を論理式でない式と仮定する。

16.1.13 論理代入式

代入式 a = ba &= ba |= b ,又は a ^= bが,論理値をもつものと仮定する。

aV であり,Va&= b などの複合代入の前で確実に代入されていない場合は,コンパイル時エラーになる必要があることに注意のこと。 上記の規則には,V は,コードの以降の時点で確実に代入されることがあることを考慮して,分離した“a は, V である”という命題が含まれている。 分離した“a は, V である”という命題が含まれていても,プログラムが受け付けられるかコンパイル時エラーになるかの判定には影響しないが,コードの中の何箇所の 場所がエラーと見なされるかに影響するので,実際には,エラー報告の質を上げることができる。

16.1.14 他の代入式

代入式 a = ba += ba -= ba *= ba /= ba %= ba <<= ba >>= ba >>>= ba &= ba |= b ,又は a ^= b が論理値をもたないと仮定する。

16.1.15 演算子 ++ 及び --

16.1.16 他の式

式が論理値をもつものではなく,かつ条件演算子式又は代入式ではない場合は,次の規則が適用される。

x のすべての直接部分式 y に対して,V は,x の前で確実に代入されているか,又は次のいずれかの一つが成り立つときにだけ,y の前で確実に代入されている。

16.2 確実な代入及び文

16.2.1 空文

16.2.2 ブロック

16.2.3 局所変数宣言文

16.2.4 ラベル付き文

16.2.5 式文

16.2.6 if

16.2.7 switch

16.2.8 while

16.2.9 do

16.2.10 for

16.2.10.1 初期化部

16.2.10.2 増分部

16.2.11 break, continue, return及びthrow

16.2.12 synchronized

16.2.13 try

V は,finally ブロックの前では,try 文の前で確実に代入されているときにだけ,確実に代入されている。


目次| |