记住那是a += x
真的意思a = a + x
。要理解的关键点是加法是从左到右计算的——也就是说,a
ina + x
是在之前计算的x
。
所以让我们弄清楚是什么b = (a += (a += a))
。首先我们使用规则a += x
mean a = a + x
,然后我们开始以正确的顺序仔细评估表达式:
b = (a = a + (a = a + a))
因为a += x
意味着a = a + x
b = (a = 1 + (a = a + a))
因为a
目前是1
。a
请记住,我们在右项之前评估左项(a = a + a)
b = (a = 1 + (a = 1 + a))
因为a
还在1
b = (a = 1 + (a = 1 + 1))
因为a
还在1
b = (a = 1 + (a = 2))
因为1 + 1
是2
b = (a = 1 + 2)
因为a
是现在2
b = (a = 3)
因为1 + 2
是3
b = 3
因为a
是现在3
这给我们留下了上面a = 3
的b = 3
理由。
让我们用另一个表达式试试这个b = (a += a) + (a += a)
:
b = (a = a + a) + (a = a + a)
b = (a = 1 + 1) + (a = a + a)
,请记住我们先评估左项,然后再评估右项
b = (a = 2) + (a = a + a)
b = 2 + (a = a + a)
现在a
是 2. 开始评估正确的术语
b = 2 + (a = 2 + 2)
b = 2 + (a = 4)
b = 2 + 4
现在a
是4
b = 6
这给我们留下了a = 4
and b = 6
。这可以通过在 Java/JavaScript 中打印出来来验证(两者在此处具有相同的行为)a
。b
将这些表达式视为解析树也可能会有所帮助。当我们评估a + (b + c)
时,LHSa
在 RHS 之前被评估(b + c)
。这是在树结构中编码的:
+
/ \
a +
/ \
b c
请注意,我们不再有任何括号——操作顺序被编码到树结构中。当我们评估树中的节点时,我们以固定的顺序处理节点的子节点(即,从左到右+
)。例如,当我们处理根节点时,我们在右子树之前+
评估左子树,无论右子树是否包含在括号中(因为括号甚至不存在于解析树中)。a
(b + c)
正因为如此,Java/JavaScript并不总是首先评估“嵌套最多的括号”,这与您可能学过的算术规则相反。
请参阅Java 语言规范:
15.7. 评估令
Java 编程语言保证运算符的操作数看起来是以特定的评估顺序进行评估的,即从左到右。
...
15.7.1. 首先评估左手操作数
在评估右侧操作数的任何部分之前,二元运算符的左侧操作数似乎已被完全评估。
如果运算符是复合赋值运算符(第 15.26.2 节),则左侧操作数的评估包括记住左侧操作数表示的变量以及获取和保存该变量的值以用于隐含的二元运算.
更多与您的问题类似的示例可以在 JLS 的链接部分中找到,例如:
示例 15.7.1-1。首先评估左手操作数
在下面的程序中,* 运算符有一个包含对变量赋值的左侧操作数和一个包含对同一变量的引用的右侧操作数。引用产生的值将反映分配首先发生的事实。
class Test1 {
public static void main(String[] args) {
int i = 2;
int j = (i=3) * i;
System.out.println(j);
}
}
该程序产生输出:
9
不允许对 * 运算符的评估产生 6 而不是 9。