Рассмотрим следующий пример:
class Quirky {
public static void main(String[] args) {
int x = 1;
int y = 3;
System.out.println(x == (x = y)); // false
x = 1; // reset
System.out.println((x = y) == x); // true
}
}
Я не уверен, есть ли в Спецификации языка Java элемент, который диктует загрузку предыдущего значения переменной для сравнения с правой стороной ( x = y
), которая в порядке, подразумеваемом скобками, должна вычисляться первой.
Почему первое выражение оценивается false
, а второе - true
? Я ожидал, (x = y)
что сначала будет вычислена оценка, а затем она сравнится x
с самой собой ( 3
) и вернется true
.
Этот вопрос отличается от порядка вычисления подвыражений в выражении Java тем, что x
здесь определенно не «подвыражение». Его нужно загружать для сравнения, а не «оценивать». Вопрос специфичен для Java, и выражение x == (x = y)
, в отличие от надуманных непрактичных конструкций, обычно создаваемых для сложных вопросов интервью, пришло из реального проекта. Предполагалось, что это будет замена одной строки для сравнения и замены
int oldX = x;
x = y;
return oldX == y;
которая, будучи даже более простой, чем инструкция x86 CMPXCHG, заслуживает более короткого выражения в Java.
источник
x = y
, безусловно, актуальна и вызывает побочный эффект, дляx
которого установлено значениеy
.Ответы:
Нет. Распространенным заблуждением является то, что круглые скобки оказывают (общее) влияние на порядок вычисления или оценки. Они только приводят части вашего выражения в определенное дерево, связывая правильные операнды с правильными операциями для задания.
(И, если вы не используете их, эта информация поступает из «приоритета» и ассоциативности операторов, что является результатом того, как определяется синтаксическое дерево языка. На самом деле, это все равно именно так, как оно работает, когда вы используйте скобки, но мы упрощаем и говорим, что тогда мы не полагаемся ни на какие правила приоритета.)
Как только это будет сделано (то есть, когда ваш код будет проанализирован в программе), эти операнды все еще должны быть оценены, и существуют отдельные правила о том, как это делается: упомянутые правила (как показал нам Эндрю) утверждают, что LHS каждой операции оценивается первым в Java.
Обратите внимание, что это не так на всех языках; например, в C ++, если вы не используете оператор короткого замыкания, такой как
&&
или||
, порядок вычисления операндов обычно не определен, и вы не должны полагаться на него в любом случае.Учителя должны прекратить объяснять приоритет оператора, используя вводящие в заблуждение фразы, такие как «это делает сложение первым». Для данного выражения
x * y + z
правильным объяснением будет «операторский приоритет делает сложение междуx * y
иz
, а не междуy
иz
», без упоминания какого-либо «порядка».источник
==
является оператором двоичного равенства .источник
Как сказал Луис Вассерман, выражение оценивается слева направо. И java не заботится о том, что на самом деле делает «define», он заботится только о том, чтобы генерировать (энергонезависимое, окончательное) значение для работы.
Таким образом, чтобы вычислить первый вывод
System.out.println()
, делается следующее:и рассчитать второе:
Обратите внимание, что второе значение всегда будет иметь значение true, независимо от начальных значений
x
иy
, поскольку вы эффективно сравниваете присвоение значения переменной, которой оно назначено, и,a = b
и,b
будет оцениваться в этом порядке, всегда будут одинаковыми по определению.источник
Там есть. В следующий раз, когда вам непонятно, что говорится в спецификации, прочитайте ее, а затем задайте вопрос, если она неясна.
Это утверждение является ложным. Скобки не подразумевают порядок оценки . В Java порядок вычисления слева направо, независимо от круглых скобок. Скобки определяют, где находятся границы подвыражения, а не порядок оценки.
Правило для
==
оператора: оценить левую сторону для получения значения, оценить правую сторону для получения значения, сравнить значения, сравнение - это значение выражения.Другими словами, значение
expr1 == expr2
всегда такое же, как если бы вы написали,temp1 = expr1; temp2 = expr2;
а затем оценилиtemp1 == temp2
.Правило для
=
оператора с локальной переменной слева: вычисление левой стороны для создания переменной, вычисление правой стороны для получения значения, выполнение присваивания, результатом является присвоенное значение.Итак, соберите это вместе:
У нас есть оператор сравнения. Оцените левую сторону, чтобы получить значение - мы получим текущее значение
x
. Оцените правую сторону: это присваивание, поэтому мы оцениваем левую сторону для создания переменной - переменнойx
- мы оцениваем правую сторону - текущее значениеy
- присваиваем ееx
, а результатом является присвоенное значение. Затем мы сравниваем исходное значениеx
со значением, которое было присвоено.Вы можете сделать
(x = y) == x
в качестве упражнения. Опять же, помните, что все правила оценки левой стороны предшествуют всем правилам оценки правой стороны .Ваше ожидание основано на ряде неверных представлений о правилах Java. Надеюсь, теперь у вас есть правильные убеждения и вы будете ожидать в будущем истинных вещей.
Это утверждение неверно. Этот вопрос совершенно уместен.
Это утверждение также ложно. Это подвыражение дважды в каждом примере.
Я без понятия что это значит.
Видимо у тебя еще много ложных убеждений. Мой совет, чтобы вы читали спецификацию, пока ваши ложные убеждения не будут заменены истинными убеждениями.
Происхождение выражения не имеет отношения к вопросу. Правила для таких выражений четко описаны в спецификации; прочитай это!
Поскольку эта однострочная замена вызывала у вас, читателя кода, большую путаницу, я бы сказал, что это был неудачный выбор. Делать код более кратким, но более сложным для понимания - не победа. Вряд ли код станет быстрее.
Кстати, в C # есть метод сравнения и замены в виде библиотечного метода, который можно сопоставить с машинной инструкцией. Я полагаю, что у Java нет такого метода, поскольку он не может быть представлен в системе типов Java.
источник
Это связано с приоритетом операторов и тем, как оцениваются операторы.
Скобки '()' имеют более высокий приоритет и имеют ассоциативность слева направо. Равенство '==' идет следующим в этом вопросе и имеет ассоциативность слева направо. Задание '=' идет последним и имеет ассоциативность справа налево.
Система использует стек для оценки выражения. Выражение оценивается слева направо.
Теперь приходит к оригинальному вопросу:
Сначала x (1) будет помещен в стек. тогда внутренняя (x = y) будет оценена и помещена в стек со значением x (3). Теперь x (1) будет сравниваться с x (3), поэтому результат равен false.
Здесь (x = y) будет оцениваться, теперь значение x станет 3, а x (3) будет помещено в стек. Теперь x (3) с измененным значением после равенства будет помещено в стек. Теперь выражение будет оценено, и оба будут одинаковыми, поэтому результат верен.
источник
Это не то же самое. Левая часть всегда будет оцениваться перед правой, и в скобках указывается не порядок выполнения, а группировка команд.
С участием:
Вы в основном делаете то же самое, что и:
И х будет иметь значение у после сравнения.
Хотя с:
Вы в основном делаете то же самое, что и:
После й взял у «s значения. И это всегда вернет истину .
источник
В первом тесте вы проверяете 1 == 3.
Во втором тесте ваша проверка делает 3 == 3.
(x = y) присваивает значение, и это значение проверяется. В первом примере сначала x = 1, затем присваивается x 3. 1 == 3?
В последнем случае х назначается 3, и, очевидно, это все еще 3. 3 == 3?
источник
Рассмотрим другой, возможно, более простой пример:
Здесь оператор предварительного увеличения
++x
должен быть применен до того, как будет выполнено сравнение - так же, как(x = y)
в вашем примере, должен быть вычислен до сравнения.Тем не менее, оценка выражения по-прежнему происходит слева направо , так что первое сравнение на самом деле,
1 == 2
а второе2 == 2
.То же самое происходит в вашем примере.
источник
Выражения оцениваются слева направо. В таком случае:
источник
По сути, первое утверждение x имеет значение 1, поэтому Java сравнивает 1 == с новой переменной x, которая не будет такой же
Во втором вы сказали x = y, что означает, что значение x изменилось, и поэтому, когда вы вызовете его снова, оно будет таким же, следовательно, почему оно истинно, а x == x
источник
== является оператором равенства сравнения и работает слева направо.
здесь старое назначенное значение x сравнивается с новым назначенным значением x, (1 == 3) // false
Принимая во внимание, что здесь новое присвоенное значение x сравнивается с новым удерживающим значением x, присвоенным ему непосредственно перед сравнением, (3 == 3) // true
Теперь рассмотрим это
Таким образом, круглые скобки играют основную роль в арифметических выражениях, а не в выражениях сравнения.
источник
x + (x = y)
и(x = y) + x
показал бы поведение, подобное оригиналу с операторами сравнения.Дело в том, что порядок приоритетов арифметических операторов / реляционных операторов из двух операторов по
=
сравнению==
с доминирующим==
(Реляционные операторы доминирует), так как он предшествует=
операторам присваивания. Несмотря на приоритет, порядок оценки - LTR (СЛЕВА ПРАВО), приоритет проявляется после порядка оценки. Итак, независимо от каких-либо ограничений оценка является LTR.источник
Легко во втором сравнении слева это присвоение после присвоения y x (слева), затем вы сравниваете 3 == 3. В первом примере вы сравниваете x = 1 с новым назначением x = 3. Кажется, что всегда принимаются текущие состояния чтения операторов слева направо от х.
источник
Вопрос, который вы задали, является очень хорошим вопросом, если вы хотите написать компилятор Java или протестировать программы, чтобы убедиться, что компилятор Java работает правильно. В Java эти два выражения должны давать результаты, которые вы видели. Например, в C ++ им это не нужно - поэтому, если кто-то повторно использует части компилятора C ++ в их компиляторе Java, вы можете теоретически обнаружить, что компилятор ведет себя не так, как должен.
Как разработчик программного обеспечения, пишущий код, который читабелен, понятен и удобен в обслуживании, обе версии вашего кода будут считаться ужасными. Чтобы понять, что делает код, нужно точно знать , как определяется язык Java. Кто-то, кто пишет код на Java и C ++, вздрогнул бы, глядя на код. Если вам нужно спросить, почему одна строка кода делает то, что делает, то вам следует избегать этого кода. (Полагаю и надеюсь, что ребята, которые правильно ответили на ваш вопрос «почему», сами тоже избежат этого кода).
источник