Java、"a[1] *= a[1] = b[n + 1]" 没有按预期工作?

Java, "a[1] *= a[1] = b[n + 1]" not working as expected?

所以正如标题所示,我有一个使用临时数组的函数,我想从另一个数组中写入一个值,然后将这两个值与自身相乘。

示例:

float[] a = {0, 0}

a[0] *= a[0] = b[n    ];
a[1] *= a[1] = b[n + 1];

我希望以上内容会执行以下操作:

a[0] = b[n    ];
a[0] *= a[0]; //(aka: a[0] = a[0] * a[0])

a[1] = b[n + 1];
a[1] *= a[1];

尽管这种行为似乎并不是实际发生的事情。相反,它似乎只是将 "a" 中的原始值乘以 "b" 中的任何值,如下所示:

a[0] = a[0] * b[n    ];
a[1] = a[1] * b[n + 1];

我一直认为,“=”之后的任何内容都会首先求值,就像您这样做时看到的那样:

float a, b;
a = b = 5;
//"a" and "b" both equal "5" now.

既然如此,那岂不是说明我的原始示例应该有效吗?

任何人都可以解释发生了什么以及为什么这段代码不能按预期工作吗?

赋值运算符(与大多数其他运算符不同)从 rightleft in Java (documentation).这意味着以下内容:

a[0] *= a[0] = b[n];

实际计算为:

a[0] *= (a[0] = b[n]);

括号中的数量是一个赋值,returns值b[n],但不改变a[0]的值。然后,进行以下最终分配:

a[0] = a[0] * b[n]

*==都是赋值运算符,具有相同的优先级。所以在这种情况下,适用从右到左的规则。

参考Java documentation:

All binary operators except for the assignment operators are evaluated from left to right; assignment operators are evaluated right to left.

那么在 a[0] *= a[0] = b[n ]; 的情况下发生的事情是,您将 a[0] 的值赋给 b[n],然后将原始 a[0] 乘以该新值.所以你的表达实际上是 a[0] *= b[n].

就我个人而言,我不会在一行中两次使用赋值运算符,这样读起来会很混乱。

我认为到目前为止的答案都不正确。发挥作用的是对 a *= b 等复合表达式的求值。简而言之,左侧的值在右侧之前计算。来自 JLS(强调我的):

At run time, the expression is evaluated in one of two ways.

If the left-hand operand expression is not an array access expression, then:

First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.

Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.

Otherwise, the result of the binary operation is converted to the type of the left-hand variable, subjected to value set conversion (§5.1.13) to the appropriate standard value set (not an extended-exponent value set), and the result of the conversion is stored into the variable.

在你的例子中:

a[0] *= a[0] = b[n    ];
  1. a[0] 的值被计算并存储在 tmp
  2. 中 计算
  3. a[0] = b[n],给出 b[n] 的值(并将 a[0] 的值更改为 b[n]
  4. tmp * a[0]计算
  5. 最后一步的结果赋值给a[0]

所以,你得到的实际上是 a[0] *= b[n]

编辑:关于作业的right-to-left-评估的混淆:我没有发现JLS,恕我直言,这是不正确的(尽管 Java 教程中使用了它)。它被称为 right-**associative* assThe JLS says this about assignments:

There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.

答案中似乎还没有指出的一个关键点是 *= 不仅仅是一个赋值运算符,而是一个 复合 赋值运算符。

The language spec 表示以下是等价的:

E1 op= E2
    <=>
E1 = (T) ((E1) op (E2))

其中 op= 类似于 *=+=

因此:

a[0] *= a[0] = b[n    ];

相当于:

a[0] = a[0] * (a[0] = b[n]);

并且 a[0](a[0] = b[n]) 之前计算,因为 left-to-right evaluation order.

所以:

  • 这从 a[0] 中读取值(称之为“A”)
  • 它从 b[n] 中读取值(称之为“B”)
  • 它将B分配给a[0] (*)
  • 然后乘以 A * B(称之为“C”)
  • 然后将 C 存储回 a[0]

所以是的,这相当于将 a[0] 中的任何内容乘以 b[n],因为上面标记 (*) 的步骤是多余的:分配的值在重新分配之前从未被读取.