Выполнение оператора 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), которое должно быть true так как x и y теперь должны иметь одни и те же ссылки, но вместо этого я получаю false.

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

Что здесь происходит?

Ответ 1

Прежде всего: это интересный вопрос, но он никогда не должен появляться в "реальном коде", поскольку присвоение переменной, которую вы вызываете в той же строке, сбивает с толку, даже если вы знаете, как она работает.

Что происходит здесь, это три шага:

  1. выяснить, какой объект вызывать метод (т.е. оценить первый x, это приведет к ссылке на строку "привет")
  2. вычислить параметры (т.е. оценить x = y, который изменит x чтобы указать на строку "до свидания", а также вернуть ссылку на эту строку)
  3. вызов метода equals результату # 1, используя результат # 2 в качестве параметра (который будет ссылаться на строки "привет" и "до свидания" соответственно).

Если посмотреть на байт-код, созданный для этого метода, он станет понятным (если вы свободно владеете байт-кодом 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, дублирует его (один раз для присвоения, один раз для возвращаемого значения выражения присваивания) и присваивает его x.

Строка № 13 вызывает equals по результату, вычисленному в строке № 9, и результат строк № 10-12.

Ответ 2

Хороший вопрос! И у 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 перед .equals, перед выражением аргумента x = y.

Поэтому ссылка на строку hello запоминается как целевая ссылка до того, как локальная переменная x будет изменена, чтобы ссылаться на строку goodbye. В результате метод equals вызывается для hello целевого объекта с аргументом goodbye, поэтому результат вызова является false.

Ответ 3

Важно помнить, что String в java является объектом и, следовательно, ссылкой. Когда вы звоните

x.equals(...)

Он проверяет, соответствует ли значение в местоположении, на которое в настоящее время ссылается x, то, что вы передаете. Внутри вы меняете значение, которое x ссылается, но вы все равно вызываете equals исходной ссылке (ссылка на "привет" "). Итак, прямо сейчас ваш код сравнивается, чтобы увидеть, соответствует ли "привет" "прощай", чего явно нет. После этой точки, если вы снова используете x, это приведет к ссылке на то же значение, что и y.

Ответ 4

x=y в скобках означает, что выражение (x=y) теперь goodbye, а внешнее x в x.equals имеет значение hello

Ответ 5

Реймус дал правильный ответ, но я хотел бы уточнить.

В Java (и большинстве языков) соглашение изменяется слева, присваивание справа.

Пусть сломает это:

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

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

Для целей отладки, а также для чтения кода всегда полезно разделить свои строки так, чтобы они делали только одно.

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

Здесь x.equals(...) вызывается по исходной ссылке на x или "hello", она обновляется для второй ссылки.

Я бы написал это как (и это даст вам ожидаемый ответ):

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

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

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

Теперь кажется очевидным, что он должен вести себя таким образом, но также очень легко увидеть, что происходит в каждой строке, к чему вам следует стремиться.

Ответ 6

Я попробовал ваш вопрос в затмении, и оба выражения верны. 1) x == (y = x) оценивается как true, так как значение x присваивается y, которое является "hello", тогда x и y сравнивают их так же,

2) x.equal(x = y) неверно, потому что значение y присваивается x, которое прощается, тогда x и x сравнивают их значение, будет отличаться, поэтому результат будет ложным

Ответ 7

Я рассматриваю вопрос в непрофессиональных терминах как "hello".equals("goodbye"). Таким образом, он возвращает false.

Ответ 8

В java String - класс.

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

представляет собой две разные строки, которые относятся к двум различным значениям, которые не совпадают, и если вы сравниваете

 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  

Ответ 9

Он видит, если x.equals (присваивать x y, возвращает true всегда), так что в основном x.equals(true)