個々の局所変数は,その値にアクセスが発生するときには,確実に代入された(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 を局所変数,a ,b ,c,及び e を式,S 及び T を文とする。
V は,値が true
である定数式が偽の場合,定数式の後で確実に代入されている。
V は,値が false
である定数式が真の場合,定数式の後で確実に代入されている。
値が true
である定数式が false
になることはなく,値が false
である定数式が true
になることはないので,上記の定義は,意味をなさないものとして成立する。
この二つの定義は,論理演算子 &&
,||
及び !
(16.1.3,16.1.4,16.1.5)を含む式の解析において役立つ。
&&
&&
b が真の場合,a が真の場合にその後で確実に代入されている,又はb が真の場合にその後で確実に代入されている,ときにだけその後で確実に代入されている。
&&
b が偽の場合,a が偽の場合にその後で確実に代入されており,かつb が偽の場合にその後で確実に代入されている,ときにだけその後で確実に代入されている。
&&
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。
||
||
b が真の場合,a が真の場合にその後で確実に代入されており,かつb が真の場合にその後で確実に代入されている,ときにだけその後で確実に代入されている。||
b が偽の場合,a が偽の場合にその後で確実に代入されている,又はb が偽の場合にその後で確実に代入されている,ときにだけその後で確実に代入されている。||
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。!
!
a が真の場合,aが偽の場合にその後で確実に代入されているときにだけ,その後で確実に代入されている。!
a が偽の場合,aが真の場合にその後で確実に代入されているときにだけ,その後で確実に代入されている。!
a の前で確実に代入されているときにだけ,a の前で確実に代入されている。&
&
b が真の場合,a が真の場合にその後で確実に代入されている,又はb が真の場合にその後で確実に代入されているときにだけ,その後で確実に代入されている。&
b が偽の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。&
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。 |
|
b が真の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。|
b が偽の場合,a が偽の場合にその後で確実に代入されている,又はb が偽の場合にその後で確実に代入されている,ときにだけその後で確実に代入されている。|
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。^
^
b が真の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。^
b が偽の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。 ^
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。==
==
b が真の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。==
b が偽の場合,少なくとも次のいずれかの一つが成り立つときにだけ,その後で確実に代入されている。==
b の前で確実に代入されているときにだけ,a の前で確実に代入されている。!=
a !=
b に対する規則は,a^
b に対する規則 (16.1.8)と同じとする。
? :
?
b :
c が真の場合,次の両方が成り立つときにだけ,その後で確実に代入されている。?
b :
c が偽の場合,次の両方が成り立つときにだけ,その後で確実に代入されている。?
b :
c の前で確実に代入されているときにだけ,aの前で確実に代入されている。
? :
?
b :
c の後で確実に代入されている。?
b :
c の前で確実に代入されているときにだけ,a の前で確実に代入されている。
代入式 a = b ,a &=
b ,a |=
b ,又は a ^=
bが,論理値をもつものと仮定する。
=
b が真の場合,a が V であるか,又は右辺の式が真の場合にV がその後で確実に代入されているときにだけ,その後で確実に代入されている。=
b が偽の場合,a が V であるか,又は右辺の式が偽の場合にV がその後で確実に代入されているときにだけ,その後で確実に代入されている。&=
b が真の場合,a が V であるか,又はa &
b が真の場合に同じ文脈の中でV がその後で確実に代入されるときにだけ,その後で確実に代入されている。&=
b が偽の場合,a が V であるか,又はa &
b が偽の場合に同じ文脈の中でV がその後で確実に代入されるときにだけ,その後で確実に代入されている。|=
b が真の場合,a が V であるか,又はa|
b が真の場合に同じ文脈の中でV がその後で確実に代入されるときにだけ,その後で確実に代入されている。|=
b が偽の場合,a が V であるか,又はa|
b が偽の場合に同じ文脈の中でV がその後で確実に代入されるときにだけ,その後で確実に代入されている。^=
b が真の場合,a が V であるか,又はa ^
b が真の場合に同じ文脈の中でVがその後で確実に代入されるときにだけ,その後で確実に代入されている。^=
b が偽の場合,a が V であるか,又はa ^
b が偽の場合に同じ文脈の中でVがその後で確実に代入されるときにだけ,その後で確実に代入されている。
a が V であり,V が a&=
b などの複合代入の前で確実に代入されていない場合は,コンパイル時エラーになる必要があることに注意のこと。
上記の規則には,V は,コードの以降の時点で確実に代入されることがあることを考慮して,分離した“a は, V である”という命題が含まれている。
分離した“a は, V である”という命題が含まれていても,プログラムが受け付けられるかコンパイル時エラーになるかの判定には影響しないが,コードの中の何箇所の 場所がエラーと見なされるかに影響するので,実際には,エラー報告の質を上げることができる。
代入式 a =
b ,a +=
b ,a -=
b ,a *=
b,a /=
b ,a %=
b ,a <<=
b ,a >>=
b ,a >>>=
b ,a &=
b ,a |=
b ,又は a ^=
b が論理値をもたないと仮定する。
++
及び --
式が論理値をもつものではなく,かつ条件演算子式又は代入式ではない場合は,次の規則が適用される。
this
,super
,及び null
に適用される。式 x のすべての直接部分式 y に対して,V は,x の前で確実に代入されているか,又は次のいずれかの一つが成り立つときにだけ,y の前で確実に代入されている。
.
),メソッド名,及び左側かっこの左側にあるオブジェクトを値として取る部分式があり,かつV がこの部分式の後で確実に代入されている場合:
S (ここでLは,ラベルとする)の後では,S の後で確実に代入されており,かつ,そのラベル付き文 L:
S から抜けることのあるすべての break
文の前で確実に代入されているときにだけ,確実に代入されている。:
S の前で確実に代入されているときにだけ,S の前で確実に代入されている。if
文if
(
e)
S の後では,S の後で確実に代入されており,かつ e が偽の場合にその後で確実に代入されているときにだけ,確実に代入されている。if
(
e)
S の前で確実に代入されているときにだけ,e の前で確実に代入されている。V は,e が真の場合にその後で確実に代入されているときにだけ,Sの前で確実に代入されている。if
(
e)
S else
T の後では,S の後で確実に代入されており,かつ T の後で確実に代入されているときにだけ,確実に代入されている。if
(
e)
S else
T の前で確実に代入されているときにだけ,e の前で確実に代入されている。V は,e が真の場合にその後で確実に代入されているときにだけ,S の前で確実に代入されている。V は,e が偽の場合にその後で確実に代入されているときにだけ,Tの前で確実に代入されている。switch
文switch
文の後で確実に代入されている。switch
ブロックが空であるか,又は V が そのswitch
ブロックの中の最後の文の後で確実に代入されている場合
switch
文から抜けることのあるすべての break
文の前で確実に代入されている場合switch
文の前で確実に代入されているときにだけ,switch式の前で確実に代入されている。switch
ブロックの中の文又は局所変数宣言文 S の前では,少なくとも次のいずれかの一つが成り立つときにだけ,確実に代入されている。while
文while
(
e)
S の後では,e が偽の場合にその後で確実に代入され,かつ while
文から抜けることのあるすべての break
文の前で確実に代入されているときにだけ,確実に代入されている。while
文の前で確実に代入されているときにだけ,eの前で確実に代入されている。do
文do
S while
(
e);
の後では,e が偽の場合にその後で確実に代入されており,かつ do
文から抜けることのあるすべての break
文の前で確実に代入されているときにだけ,確実に代入されている。do
文の前で確実に代入されているときにだけ,Sの前で確実に代入されている。do
文の本体から抜けることのあるすべての continue
文の前で確実に代入されているときにだけ,e の前で確実に代入されている。for
文for
文の後で確実に代入されている。for
文の初期化部の前では,そのfor
文の前で確実に代入されているときにだけ,確実に代入されている。for
文の条件部の前では,そのfor
文の初期化部の後で確実に代入されているときにだけ,確実に代入されている。for
文の増分部の前では,V が含まれる文の後で確実に代入されており,かつV が for
文の本体から抜けることのあるすべての continue
文の前で確実に代入されているときにだけ,確実に代入されている。for
文の初期化部が局所変数宣言文である場合,16.2.3の規則が適用される。break
, continue
, return
及びthrow
文break
文,continue
文,return
文,又は throw
文の後で確実に代入されていると表現する。
変数が文又は式の“後で確実に代入されている”とは,“文又は式が正常完了した後で確実に代入されている”ことを意味する。
break
文,continue
文,return
文,又は throw
文は正常完了することがないので,これは意味をなさないものとして成立する。return
文又は throw
文において,Vは,その式の前では, そのreturn
文又は throw
文の前で確実に代入されているときにだけ,確実に代入されている。synchronized
文synchronized
(
e)
S の後では,S の後で確実に代入されているときにだけ,確実に代入されている。synchronized
(
e)
S 文の前で確実に代入されているときにだけ,e の前で確実に代入されている。try
文try
文の後で確実に代入されている。try
ブロックの後で確実に代入されており,かつV が try
文のすべての catch
ブロックの後で確実に代入されている場合try
文に finally
ブロックがあり,V が finally
ブロックの後で確実に代入されている場合try
ブロックの前では,try
文の前で確実に代入されているときにだけ,確実に代入されている。catch
ブロックの前では,try
文の前で確実に代入されているときにだけ,確実に代入されている。
V は,finally
ブロックの前では,try
文の前で確実に代入されているときにだけ,確実に代入されている。