web-dev-qa-db-ja.com

Java割り当て演算子の実行

Javaでは、割り当てが正しいオペランドの値に評価されることを理解しているため、x == (y = x)などのステートメントはtrueに評価されます。

ただし、このコードはfalseを出力します。

public static void main(String[]args){
    String x = "hello";
    String y = "goodbye";
    System.out.println(x.equals(x = y));
}

どうしてこれなの?私の理解では、最初に(x = y)を評価し、xの値にyを割り当て、次にyの値を返します。次にx.equals(y)が評価されます。truexは同じ参照を共有するはずなので、yになりますが、代わりにfalseになります。

Screenshot showing the source and that the output is "false"

ここで何が起きてるの?

74
Sam

まず第一に、それは興味深い質問ですが、「実際のコード」では決して出てはいけません。同じ行で呼び出す変数に代入するのは、たとえそれがどのように機能するかを知っていても混乱させるからです。

ここで何が起こるかは、次の3つのステップです。

  1. メソッドを呼び出すオブジェクトを特定します(つまり、最初のxを評価します。これにより、文字列 "hello"への参照が生成されます)
  2. パラメーターを把握します(つまり、x = yを評価します。これは、xを変更して、ストリング "goodbye"を指し、そのストリングへの参照も返します)
  3. #2の結果をパラメーターとして使用して、#1の結果に対してメソッドequalsを呼び出します(これは、それぞれストリング「hello」および「goodbye」への参照になります)。

そのメソッド用に生成されたバイトコードを見ると、明確になります(Javaバイトコードに堪能であると仮定):

     0: ldc           #2                  // String hello
     2: astore_1
     3: ldc           #3                  // String goodbye
     5: astore_2
     6: getstatic     #4                  // Field Java/lang/System.out:Ljava/io/PrintStream;
     9: aload_1
    10: aload_2
    11: dup
    12: astore_1
    13: invokevirtual #5                  // Method Java/lang/String.equals:(Ljava/lang/Object;)Z
    16: invokevirtual #6                  // Method Java/io/PrintStream.println:(Z)V
    19: return

行#9は上記のステップ1です(つまり、xを評価し、値を記憶しています)。

行#10-12はステップ2です。yをロードし、複製して(割り当てに1回、割り当て式の戻り値に1回)、xに割り当てます。

行#13は、行#9で計算された結果と行#10-12の結果に対してequalsを呼び出します。

74
Joachim Sauer

良い質問! JLSには答えがあります...

§15.12.4.1 (例15.12.4.1-2)。メソッド呼び出し中の評価順序:

インスタンスメソッドの呼び出しの一部として、呼び出されるオブジェクトを示す式があります。この式は、メソッド呼び出しに対する引数式の一部が評価される前に完全に評価されるようです。

だから、で:

String x = "hello";
String y = "goodbye";
System.out.println(x.equals(x = y));

.equalsの前のxの出現は、引数式x = yの前に最初に評価されます。

したがって、ローカル変数helloがストリングxを参照するように変更される前に、ストリングgoodbyeへの参照がターゲット参照として記憶されます。その結果、ターゲットオブジェクトequalsに対してhelloメソッドが引数goodbyeとともに呼び出されるため、呼び出しの結果はfalseになります。

37

JavaのStringはオブジェクトであり、したがって参照であることを覚えておくことが重要です。電話するとき

x.equals(...)

現在xによって参照されている場所の値が、渡される値と等しいかどうかをチェックしています。内部では、xreferenceencingである値を変更していますが、 original参照( "hello"への参照)でequalsを呼び出しています。ですから、今、あなたのコードは「hello」が「goodbye」と等しいかどうかを確認するために比較していますが、明らかにそうではありません。この後、xを再度使用すると、yと同じ値への参照が発生します。

27
Keveloper

x=yは、式(x=y)goodbyeになり、x.equalsの外側のxが値helloを保持することを意味します

5

レイマスは正しい答えを出しましたが、詳しく説明したいと思います。

Java(およびほとんどの言語)では、規則は左が変数、右が割り当てです。

分解しましょう:

String x = "hello";
//x <- "hello"

String y = "goodbye";
//y <- "goodbye";

デバッグの目的とコードの読みやすさのために、行を分割して1つのことだけを行うのは常に良い習慣です。

System.out.println(x.equals(x = y)); //Compound statement

ここで、x.equals(...)はxへの元の参照、または「hello」で呼び出され、2番目の参照用に更新されます。

私はこれを次のように書くでしょう(そして、これはあなたにあなたの期待される答えを与えるでしょう):

x = y;
// x <- y = "goodbye"

boolean xEqualsX = x.equals(x);
// xEqualsX <- true

System.out.println(xEqualsX);
// "true"

これは、このように動作する必要があることは明らかですが、各行で何が起こっているかを正確に確認することも非常に簡単です。

4

Eclipseであなたの質問を試しましたが、両方の表現が正しいです。 1)x ==(y = x)trueと評価されます。これは、xの値が「hello」であるyに割り当てられ、xとyが比較されるため、結果がtrueになるため、trueになります。

2)x.equal(x = y)それは偽です。なぜなら、yの値はさよならであるxに割り当てられ、xとxはそれらの値を比較し、結果が偽になるからです。

2
Alishan

質問は素人用語では"hello".equals("goodbye")と見られます。したがって、falseを返します。

1
Vamsi

Javaでは、文字列はクラスです。

String x = "hello";
String y = "goodbye"; 

同じではない2つの異なる値を参照する2つの異なる文字列であり、比較する場合

 System.out.println(x.equals(x = y)); 
//this compare value (hello and goodbye) return true

    System.out.println(x == (y = x)); 
// this compare reference of an object (x and y) return false  
1
Aadesk