クラス変数への前方参照を管理する正確なルールは、JLSのセクション§8.3.2.3で説明されています。
8.3.2.3初期化中のフィールドの使用に関する制限
メンバーの宣言は、メンバーがstatic
クラスまたはインターフェイスCのインスタンス(それぞれ)フィールドであり、次のすべての条件が満たされる場合にのみ、使用される前にテキストで表示される必要があります。
- 使用法は、Cのインスタンス(それぞれ
static
)変数初期化子またはCのインスタンス(それぞれstatic
)初期化子で発生します。
- 使用法は、割り当ての左側にはありません。
- 使用法は単純な名前によるものです。
- Cは、使用法を囲む最も内側のクラスまたはインターフェースです。
上記の4つの要件のいずれかが満たされない場合、コンパイル時エラーが発生します。
これは、コンパイル時エラーがテストプログラムの結果であることを意味します。
class Test {
int i = j; // compile-time error: incorrect forward reference
int j = 1;
}
一方、次の例はエラーなしでコンパイルされます。
class Test {
Test() { k = 2; }
int j = 1;
int i = j;
int k;
}
Testのコンストラクター
(§8.8)が3行後に宣言されたフィールドkを参照している場合でも。
これらの制限は、コンパイル時に、循環またはその他の不正な形式の初期化をキャッチするように設計されています。したがって、両方:
class Z {
static int i = j + 2;
static int j = 4;
}
と:
class Z {
static { i = j + 2; }
static int i, j;
static { j = 4; }
}
コンパイル時エラーが発生します。メソッドによるアクセスはこの方法ではチェックされないため、次のようになります。
class Z {
static int peek() { return j; }
static int i = peek();
static int j = 1;
}
class Test {
public static void main(String[] args) {
System.out.println(Z.i);
}
}
出力を生成します:
0
iの変数初期化子は、クラスメソッドpeekを使用して、jが変数初期化子によって初期化される前に変数jの値にアクセスするため、その時点ではまだデフォルト値(§4.12.5)があります。