Выполнение оператора 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
.
Что здесь происходит?
Ответы
Ответ 1
Прежде всего: это интересный вопрос, но он никогда не должен появляться в "реальном коде", поскольку присвоение переменной, которую вы вызываете в той же строке, сбивает с толку, даже если вы знаете, как она работает.
Что происходит здесь, это три шага:
- выяснить, какой объект вызывать метод (т.е. оценить первый
x
, это приведет к ссылке на строку "привет") - вычислить параметры (т.е. оценить
x = y
, который изменит x
чтобы указать на строку "до свидания", а также вернуть ссылку на эту строку) - вызов метода
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)