以下はコンパイルされず、「不正な前方参照」メッセージが表示されます。
class StaticInitialisation {
static
{
System.out.println("Test string is: " + testString);
}
private static String testString;
public static void main(String args[]) {
new StaticInitialisation();
}
}
ただし、以下はコンパイルされます。
class InstanceInitialisation1 {
{
System.out.println("Test string is: " + this.testString);
}
private String testString;
public static void main(String args[]) {
new InstanceInitialisation1();
}
}
ただし、以下はコンパイルされず、「不正な前方参照」メッセージが表示されます。
class InstanceInitialisation2 {
private String testString1;
{
testString1 = testString2;
}
private String testString2;
public static void main(String args[]) {
new InstanceInitialisation2();
}
}
InstanceInitialisation1がコンパイルするのに、StaticInitialisationとInstanceInitialisation2がコンパイルされないのはなぜですか?
これは、JLSのセクション 8.3. でカバーされています。
これらのクラス変数がスコープ内にある場合でも、使用後に宣言がテキストで表示されるクラス変数の使用が制限される場合があります(§6.3)。具体的には、次のすべてが当てはまる場合、コンパイル時エラーになります。
クラスまたはインターフェイスCでのクラス変数の宣言は、クラス変数の使用後にテキストで表示されます。
使用法は、Cのクラス変数初期化子またはCの静的初期化子のいずれかでの単純な名前です。
使用は割り当ての左側ではありません。
Cは、使用を囲む最も内側のクラスまたはインターフェースです。
インスタンス変数がスコープ内にある場合でも、使用後に宣言がテキストで表示されるインスタンス変数の使用が制限される場合があります。具体的には、次のすべてが当てはまる場合、コンパイル時エラーになります。
クラスまたはインターフェイスCでのインスタンス変数の宣言は、インスタンス変数の使用後にテキストで表示されます。
使用法は、Cのインスタンス変数初期化子またはCのインスタンス初期化子のいずれかでの単純な名前です。
使用は割り当ての左側ではありません。
Cは、使用を囲む最も内側のクラスまたはインターフェースです。
2番目のケースでは、使用は単純な名前ではありません-明示的にthis
を取得します。つまり、上記の2番目のリストの2番目の箇条書きに準拠していないため、エラーは発生しません。
次のように変更した場合:
System.out.println("Test string is: " + testString);
...その後はコンパイルされません。
または、反対方向に、静的初期化ブロックのコードを次のように変更できます。
System.out.println("Test string is: " + StaticInitialisation.testString);
奇妙ですが、それがその方法です。
単純な理由-すべての前方参照を分析して禁止するのは費用がかかりすぎるか不可能です。例えば.
{
print( getX(); ); // this.x
print( that().x ); // this.x
}
int x;
int getX(){ return x; }
This that(){ return this; }
仕様は、一般的なプログラマーの間違いを示すいくつかの単純なケースを禁止することで決着します。
これらの2つの例を見てみましょう。これで、明確になると思います。
public class InstanceAndSataticInit {
{
System.out.println("Test string is (instance init): " + this.testString);
}
static{
System.out.println("Test string is (static init ): " + InstanceAndSataticInit.testStringStatic);
}
public static String testStringStatic="test";
public String testString="test";
public static void main(String args[]) {
new InstanceAndSataticInit();
}
}
出力:
Test string is (static init ): null
Test string is (instance init): null
そして
public class InstanceAndSataticInitVariableFirst {
public static String testStringStatic="test";
public String testString="test";
{
System.out.println("Test string is (instance init): " + this.testString);
}
static{
System.out.println("Test string is (static init ): " + InstanceAndSataticInitVariableFirst.testStringStatic);
}
public static void main(String args[]) {
new InstanceAndSataticInitVariableFirst();
}
}
出力:
Test string is (static init ): test
Test string is (instance init): test
つまり、シーケンスは次のようになります。
静的変数は作成されますが、初期化されません。
静的初期化は、指定されたシーケンスに従って実行されます。
シーケンスとは、コード内の外観を意味します。
この手順はあなたの2つが機能しない例StaticInitialisation
およびInstanceInitialisation2
に答えると思います
しかし、2番目の作業例InstanceInitialisation1
this
キーワードを使用する場合、実際にはコンパイラがテキスト階層を見落とすのを助けています。最初の例でInstanceAndSataticInit.testStringStatic
を呼び出すと、static
の場合にも同じことが起こりますInstanceAndSataticInit
ここで理解しなければならないのは、2番目のコードスニペットでブロックとこのキーワードを使用しているということです。