为什么 x == (x = y) 不同于 (x = y) == x?

212

考虑以下示例:

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;

Java中,由于比x86 CMPXCHG指令更简单,因此应该使用更短的表达方式。


63
左侧总是在右侧之前被计算。括号对此没有影响。 - Louis Wasserman
11
评估表达式 x = y 是非常相关的,会导致副作用,即将 x 设置为 y 的值。 - Louis Wasserman
51
请勿在对状态进行检查的同一行中混合状态改变的操作,这会极大地降低代码的可读性,请为自己和团队成员着想。(当需要满足原子性要求时可能存在必须这样做的情况,但是已经存在用于此目的的函数,并且它们的作用会立即被认出。) - jpmc26
50
真正的问题是为什么你想要写出这样的代码。 - klutt
26
你问题的关键在于你错误地认为括号意味着表达式的计算顺序。这是一个普遍的信仰,因为我们在小学数学和一些初学者编程书籍中的教学方式是这样的,但这是个错误的信仰。这是一个相当常见的问题。你可以通过阅读我关于该主题的文章获益; 它们是关于C#的,但同样适用于Java: https://ericlippert.com/2008/05/23/precedence-vs-associativity-vs-order/ https://ericlippert.com/2009/08/10/precedence-vs-order-redux/ - Eric Lippert
显示剩余22条评论
14个回答

4

== 是一种比较相等的运算符,它从左到右执行。

x == (x = y);

这里将变量x的旧赋值与新赋值进行比较,(1==3)//false

(x = y) == x;

在这里,新分配给 x 的值与比较之前分配给它的新持有值进行比较,(3==3)//true

现在考虑这个例子

    System.out.println((8 + (5 * 6)) * 9);
    System.out.println(8 + (5 * 6) * 9);
    System.out.println((8 + 5) * 6 * 9);
    System.out.println((8 + (5) * 6) * 9);
    System.out.println(8 + 5 * 6 * 9);

输出:

342

278

702

342

278

因此,括号仅在算术表达式中起主要作用,而不在比较表达式中起作用。


1
结论是错误的。 算术运算符和比较运算符之间的行为没有区别。 x +(x = y)(x = y) + x 与比较运算符的原始行为类似。 - JJJ
1
@JJJ 在 x+(x=y) 和 (x=y)+x 中,没有涉及比较操作,只是将 y 的值赋给 x 并将其加到 x 上。 - Nisrin Dhoondia
1
是的,这就是重点。"括号只在算术表达式中起主要作用,而不在比较表达式中"是错误的,因为算术表达式和比较表达式之间没有区别。 - JJJ

2
这里的问题是算术运算符/关系运算符优先级顺序中,两个运算符“=”和“==”中占主导地位的是“==”(关系运算符),因为它在“=”赋值运算符之前。尽管有优先级,但评估顺序是从左到右的,优先级仅在评估顺序之后发挥作用。因此,无论任何限制,评估都是从左到右的。

1
答案是错误的。运算符优先级不影响求值顺序。阅读一些得票最高的答案以获取解释,特别是这个 - JJJ
1
正确,实际上我们被教导了优先级限制的错觉,但正确指出它没有影响,因为评估顺序仍然是从左到右。 - Himanshu

-1

在左边的第二个比较中,将y分配给x(在左边)后,您随后比较3 == 3,这很容易。在第一个示例中,您正在将x = 1与新分配的x = 3进行比较。似乎总是从左到右读取x语句的当前状态。


-2
你问的这种问题如果你想写一个Java编译器,或者测试程序来验证Java编译器是否正常工作,那么这是个非常好的问题。在Java中,这两个表达式必须产生您看到的结果。例如,在C++中,它们就不一定会产生相同的结果 - 因此,如果有人在他们的Java编译器中重复使用了C++编译器的部分代码,您可能理论上会发现编译器的行为与预期不同。
作为一名软件开发人员,编写易读、易懂和易维护的代码,您应该认为您提供的这两种版本的代码都很糟糕。要理解代码的功能,必须确切地知道Java语言的定义。对于同时编写Java和C++代码的人来说,看到这样的代码肯定会感到震惊。如果您必须询问单行代码的原因,则应避免使用该代码。(我想并希望回答您“为什么”问题的人也会避免使用这种代码)。

要理解代码的作用,必须准确了解Java语言的定义。但是如果每个同事都认为这是常识呢? - Kindred

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接