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)
が評価されます。true
とx
は同じ参照を共有するはずなので、y
になりますが、代わりにfalse
になります。
ここで何が起きてるの?
まず第一に、それは興味深い質問ですが、「実際のコード」では決して出てはいけません。同じ行で呼び出す変数に代入するのは、たとえそれがどのように機能するかを知っていても混乱させるからです。
ここで何が起こるかは、次の3つのステップです。
x
を評価します。これにより、文字列 "hello"への参照が生成されます)x = y
を評価します。これは、x
を変更して、ストリング "goodbye"を指し、そのストリングへの参照も返します)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
を呼び出します。
良い質問! 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
になります。
JavaのString
はオブジェクトであり、したがって参照であることを覚えておくことが重要です。電話するとき
x.equals(...)
現在x
によって参照されている場所の値が、渡される値と等しいかどうかをチェックしています。内部では、x
がreferenceencingである値を変更していますが、 original参照( "hello"への参照)でequals
を呼び出しています。ですから、今、あなたのコードは「hello」が「goodbye」と等しいかどうかを確認するために比較していますが、明らかにそうではありません。この後、x
を再度使用すると、yと同じ値への参照が発生します。
x=y
は、式(x=y)
がgoodbye
になり、x.equals
の外側のxが値hello
を保持することを意味します
レイマスは正しい答えを出しましたが、詳しく説明したいと思います。
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"
これは、このように動作する必要があることは明らかですが、各行で何が起こっているかを正確に確認することも非常に簡単です。
Eclipseであなたの質問を試しましたが、両方の表現が正しいです。 1)x ==(y = x)trueと評価されます。これは、xの値が「hello」であるyに割り当てられ、xとyが比較されるため、結果がtrueになるため、trueになります。
2)x.equal(x = y)それは偽です。なぜなら、yの値はさよならであるxに割り当てられ、xとxはそれらの値を比較し、結果が偽になるからです。
質問は素人用語では"hello".equals("goodbye")
と見られます。したがって、falseを返します。
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