4

私は2つのクラスの参照を相互に保存する問題に取り組んでいました

例えば:

class A {
B b;
A(B b){
this.b = b;}
}

class B {
A a;
B(A a){
this.a = a;}
}

public static void main(String...s){
A a = new A(new B(null));
a.b.a = a;
}

上記の初期化の代わりに、以下のステートメントを使用すると:

A a = new A(new B(a));

私は非常に明白な以下のエラーを得ました:

Main.java:19: error: variable a might not have been initialised
        A a = new A(new B(a));

しかし、JShellで同じことを試してみると、問題なく動作します(初期化されていないことを念のために確認するために、以前に初期化されていないことを確認するステートメントを実行する直前variable aにチェックしました:variable a

ここに画像の説明を入力

ここで何かが足りないかもしれませんが、JAVA で実行されている同じステートメントの 2 つの異なる動作がある理由を理解するのを手伝ってくれませんか。

この問題を理解する簡単な方法は、以下のステートメントは通常のプログラムでは許可されていますJshellが、通常のプログラムでは許可されていないということです。

var somevar = somevar;
4

2 に答える 2

10

ステートメントA a = new A(new B(a));は、ローカル変数の宣言ではありません。

ただし、最初に、説明している問題を次のように単純化できます。

jshell> int a = a;
a ==> 0

さて、それはどのように機能しましたか?

まあ、JEP 222のスニペットが言うように:

JShell では、「変数」は格納場所であり、関連する型があります。変数は、FieldDeclarationスニペットで明示的に作成されます。

int a = 42;

または式によって暗黙的に (以下を参照)。変数には、少量のフィールド セマンティクス/構文があります(たとえば、volatile修飾子が許可されています)。ただし、変数にはそれらを囲むユーザーに表示されるクラスがなく、通常はローカル変数のように表示および使用されます。

そのため、フィールドやローカル変数のように振る舞います。

フィールドと同様ですが、ローカル変数とは異なり、初期化子のない宣言はデフォルト値を割り当てます。

jshell> int b;
b ==> 0

しかし、に戻りint a = a;ます。JEP 222 のセクション状態は次のように述べています。

JShell の状態は、JShell のインスタンスに保持されます。スニペットはメソッドを使用して JShell で評価され、eval(...)エラーが生成され、コードが宣言され、ステートメントまたは式が実行されます。初期化子を持つ変数の場合、宣言と実行の両方が発生します

したがって、変数の宣言と初期化子の実行は、2 つの別個のアクションです。

これは、イニシャライザが実行されるときに、変数がすでに宣言されており、デフォルト値が割り当てられていることを意味します。

実際にint a = a;は、次のように評価されます。

jshell> int a;
a ==> 0

jshell> a = a;
a ==> 0

これが、jshell REPL が機能するように設計されている方法です。これはバグではありません。

于 2018-03-31T07:51:16.133 に答える