web-dev-qa-db-ja.com

前方参照に「this」キーワードを使用する必要があるのはなぜですか?

クラス内の非静的変数にアクセスするために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
}
59
Nomad

変数が最初に宣言され、次に割り当てられます。そのクラスはこれと同じです:

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  
45
Jason

詳細な説明は 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を使用してコンパイラエラーを回避する必要がある場合は、後の人が保守するのが非常に難しいコードを書いている可能性があります。

71
Erwin Bolwidt

3つのケースを提示しました:

  1. int a = b; int b;
    これは、コンパイラがメモリ内でbを探し、そこに存在しないため、エラーになります。ただし、thisキーワードを使用すると、bがクラスのスコープで定義されることを明示的に指定し、すべてのクラス参照が検索され、最終的に検索されます。
  2. 2番目のシナリオは非常に単純で、説明したように、bcの前のスコープで定義され、メモリ内でbを探している間は問題になりません。
  3. int var1 = this.var2;
    int var2 = this.var1;
    この場合エラーは発生しません。それぞれの場合、変数はクラスで定義され、割り当てはthisを使用するため、後続のコンテキストだけでなく、クラスで割り当てられた変数を検索します。
4
Wajid Ali

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 ?_であるbaの後に宣言され、コンパイル時エラーと見なされるため、_Illegal Forward Reference_がコンパイル時エラーを与えると言いました。 。

しかし、methodsの場合、_Forward Reference_はlegalになります

_int a = test();
int b;

int test() {
    return 0;
}
_

しかし、私のコードでは、引数を持つコンストラクターはabの両方の前に宣言されていますが、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は存在し、以前に宣言された存在しない属性を探します。

4
Arun Sudhakaran

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を使用して名前を指定する必要があります。

3
leshkin