考虑下面的例子:

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)进行比较,根据括号所暗示的顺序,应该首先计算右边的值。

为什么第一个表达式的值为假,而第二个表达式的值为真?我希望(x = y)首先被求值,然后它将x与自身(3)进行比较并返回true。


这个问题与Java表达式中子表达式的求值顺序不同,因为x在这里肯定不是“子表达式”。它需要被加载来进行比较,而不是被“评估”。这个问题是java特有的,表达式x == (x = y)与通常为棘手的面试问题而设计的牵强的不切实际的构造不同,它来自一个真实的项目。它应该是比较-替换习惯用法的一行替换

int oldX = x;
x = y;
return oldX == y;

它比x86的CMPXCHG指令更简单,在Java中应该有更短的表达式。


当前回答

考虑另一个更简单的例子:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

在这里,必须在进行比较之前应用++x中的前递增操作符-就像在您的示例中(x = y)必须在进行比较之前计算一样。

但是,表达式求值仍然从左→→右,所以第一个比较实际上是1 == 2,而第二个比较是2 == 2。 同样的事情也发生在你的例子中。

其他回答

正如LouisWasserman所说,表达式从左到右求值。java并不关心“evaluate”实际上做了什么,它只关心生成一个(非易失性的,最终的)值。

//the example values
x = 1;
y = 3;

因此,要计算System.out.println()的第一个输出,需要执行以下操作:

x == (x = y)
1 == (x = y)
1 == (x = 3) //assign 3 to x, returns 3
1 == 3
false

为了计算第二个:

(x = y) == x
(x = 3) == x //assign 3 to x, returns 3
3 == x
3 == 3
true

请注意,不管x和y的初始值如何,第二个值的值总是为true,因为您实际上是在将值的赋值与赋值的变量进行比较,并且根据定义,a = b和b的赋值顺序总是相同的。

表达式从左到右求值。在这种情况下:

int x = 1;
int y = 3;

x == (x = y)) // false
x ==    t

- left x = 1
- let t = (x = y) => x = 3
- x == (x = y)
  x == t
  1 == 3 //false

(x = y) == x); // true
   t    == x

- left (x = y) => x = 3
           t    =      3 
-  (x = y) == x
-     t    == x
-     3    == 3 //true

根据括号所示的顺序,哪个应该先计算

不。一个常见的误解是,括号对计算或求值顺序有任何(一般的)影响。它们只是将表达式的部分强制到特定的树中,将正确的操作数绑定到作业的正确操作。

(而且,如果不使用它们,这些信息来自操作符的“优先级”和结合性,这是语言语法树定义的结果。事实上,当你使用圆括号时,它仍然是这样工作的,但我们简化了,说我们不依赖于任何优先规则。)

一旦完成了这一点(即一旦你的代码被解析成一个程序),这些操作数仍然需要被求值,并且有关于如何做的单独的规则:所说的规则(正如Andrew向我们展示的那样)声明每个操作的LHS在Java中首先被求值。

注意,并非所有语言都是如此;例如,在c++中,除非使用&&或||这样的短路操作符,否则操作数的求值顺序通常是未指定的,无论如何都不应该依赖于它。

老师们需要停止使用像“这使得加法先发生”这样的误导性短语来解释运算符优先级。给定一个表达式x * y + z,正确的解释应该是“运算符优先级使加法发生在x * y和z之间,而不是在y和z之间”,没有提到任何“顺序”。

考虑另一个更简单的例子:

int x = 1;
System.out.println(x == ++x); // false
x = 1; // reset
System.out.println(++x == x); // true

在这里,必须在进行比较之前应用++x中的前递增操作符-就像在您的示例中(x = y)必须在进行比较之前计算一样。

但是,表达式求值仍然从左→→右,所以第一个比较实际上是1 == 2,而第二个比较是2 == 2。 同样的事情也发生在你的例子中。

基本上第一个表述x的值是1 Java将1 ==和新的x变量进行比较,这两个变量不一样

在第二个例子中,你说x=y,这意味着x的值改变了,所以当你再次调用它时,它将是相同的值,因此它是正确的,x= =x