クラス内の非静的変数にアクセスするためにthis
キーワードを使用すると、Javaはエラーになりません。しかし、使用しない場合、Javaはエラーを出します。なぜthis
を使用する必要があるのですか?
いつthis
を使用すべきかはわかっていますが、この例は通常の使用法とは大きく異なります。
例:
class Foo {
// int a = b; // gives error. why ?
int a = this.b; // no error. why ?
int b;
int c = b;
int var1 = this.var2; // very interesting
int var2 = this.var1; // very interesting
}
変数が最初に宣言され、次に割り当てられます。そのクラスはこれと同じです:
class Foo {
int a;
int b;
int c = b;
int var1;
int var2;
public Foo() {
a = b;
var1 = var2;
var2 = var1;
}
}
できない理由int a = b;
は、オブジェクトの作成時にb
がまだ定義されていないが、オブジェクト自体(つまりthis
)がそのすべてのメンバー変数とともに存在するためです。
それぞれの説明は次のとおりです。
int a = b; // Error: b has not been defined yet
int a = this.b; // No error: 'this' has been defined ('this' is always defined in a class)
int b;
int c = b; // No error: b has been defined on the line before
詳細な説明は Java言語仕様: "フィールド初期化中の前方参照" のセクション8.3.3にあります。
前方参照(その時点でまだ宣言されていない変数を参照)は、次のすべてに該当する場合にのみエラーになります。
クラスまたはインターフェイスCでのインスタンス変数の宣言は、インスタンス変数の使用後にテキストで表示されます。
使用法は、Cのインスタンス変数初期化子またはCのインスタンス初期化子のいずれかでsimple nameです。
使用は割り当ての左側にはありません。
Cは、使用を囲む最も内側のクラスまたはインターフェイスです。
太字のテキスト「使用は単純な名前です」を参照してください。単純な名前は、さらに修飾されていない変数名です。コードでは、b
は単純な名前ですが、this.b
はそうではありません。
その理由は、JLSの例の筆記体のテキストが示すように、
「上記の制限は、コンパイル時に循環または不正な初期化をキャッチするように設計されています。」
言い換えれば、修飾された参照はあなたが何をしているのかを注意深く考えている可能性が高いと考えているため、this.b
を許可しますが、単にb
を使用することはおそらく間違いを犯したことを意味します。
これが、Java言語の設計者の理論的根拠です。実際にそれが真実であるかどうかは、私の知る限り、研究されていません。
上記を拡張するには、質問に対するDukelingのコメントを参照して、修飾された参照this.b
を使用しても、期待する結果が得られない可能性があります。
OPはインスタンス変数のみを参照するため、この説明はインスタンス変数に限定しています。インスタンス変数が割り当てられる順序は JLS 12.5新しいクラスインスタンスの作成 で説明されています。スーパークラスコンストラクターが最初に呼び出され、初期化コード(割り当てと初期化ブロック)がテキスト順に実行されることを考慮する必要があります。
だから与えられた
int a = this.b;
int b = 2;
a
がゼロ(b
の初期化子が実行されたときのa
の値)およびb
が2になることになります。
スーパークラスコンストラクターがサブクラスでオーバーライドされているメソッドを呼び出し、そのメソッドがb
に値を割り当てると、さらに奇妙な結果が得られます。
そのため、一般に、コンパイラを信じてフィールドを並べ替えるか、循環初期化の場合に根本的な問題を修正することをお勧めします。
this.b
を使用してコンパイラエラーを回避する必要がある場合は、後の人が保守するのが非常に難しいコードを書いている可能性があります。
3つのケースを提示しました:
int a = b; int b;
b
を探し、そこに存在しないため、エラーになります。ただし、this
キーワードを使用すると、b
がクラスのスコープで定義されることを明示的に指定し、すべてのクラス参照が検索され、最終的に検索されます。b
はc
の前のスコープで定義され、メモリ内でb
を探している間は問題になりません。int var1 = this.var2;
int var2 = this.var1;
this
を使用するため、後続のコンテキストだけでなく、クラスで割り当てられた変数を検索します。Javaのクラスの場合、this
はデフォルトの参照変数(特定の参照が指定されていない場合)であり、ユーザーが指定するか、コンパイラーが非静的ブロック内で提供します。例えば
_public class ThisKeywordForwardReference {
public ThisKeywordForwardReference() {
super();
System.out.println(b);
}
int a;
int b;
public ThisKeywordForwardReference(int a, int b) {
super();
this.a = a;
this.b = b;
}
}
_
Javaの_int a = b; // gives error. why ?
_であるb
がa
の後に宣言され、コンパイル時エラーと見なされるため、_Illegal Forward Reference
_がコンパイル時エラーを与えると言いました。 。
しかし、methods
の場合、_Forward Reference
_はlegalになります
_int a = test();
int b;
int test() {
return 0;
}
_
しかし、私のコードでは、引数を持つコンストラクターはa
とb
の両方の前に宣言されていますが、System.out.println(b);
はSystem.out.println(this.b);
コンパイラー。
キーワードthis
は、現在のクラス参照、またはメソッド、コンストラクター、または属性がアクセスされる参照を単に意味します。
_A a1 = new A(); // Here this is nothing but a1
a1.test(); // Here this is again a1
_
_a = this.b;
_と言うときは、b
が現在のクラス属性であることを指定しますが、_a = b;
_と言うときは、非静的ブロック内にないためthis
は存在し、以前に宣言された存在しない属性を探します。
Java言語仕様: https://docs.Oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3 .2.
これが理由です、IMO:The usage is via a simple name.
したがって、この場合、this
を使用して名前を指定する必要があります。