一尘不染

Java中评估顺序的规则是什么?

java

我正在阅读一些Java文本,并获得以下代码:

int[] a = {4,4};
int b = 1;
a[b] = b = 0;

在本文中,作者没有给出明确的解释,最后一行的效果是: a[1] = 0;

我不确定自己是否理解:评估是如何发生的?


阅读 608

收藏
2020-02-27

共1个答案

一尘不染

让我说得很清楚,因为人们一直误会这一点:

子表达式的求值顺序与关联性和优先级无关。结合性和优先级确定以什么顺序运营商执行,但不以什么顺序确定的子表达式进行评估。你的问题与子表达式的计算顺序有关。

考虑一下A() + B() + C() * D()。乘法比加法具有更高的优先级,并且加法是左关联的,所以这等效于(A() + B()) + (C() * D()) But。但是,知道这仅告诉你第一次加法将在第二次加法之前发生,并且乘法将在第二次加法之前发生。它不会告诉你将以什么顺序调用A(),B(),C()和D()!(它也不会告诉你的乘法运算是之前还是先添加后发生的。)这将是完全有可能遵守规则的优先级和结合通过编写此为:

d = D()          // these four computations can happen in any order
b = B()
c = C()
a = A()
sum = a + b      // these two computations can happen in any order
product = c * d
result = sum + product // this has to happen last

在那里遵循所有优先级和关联性规则-第一个加法发生在第二个加法之前,而乘法发生在第二个加法之前。显然,我们可以以任何顺序调用A(),B(),C()和D(),并且仍然遵守优先级和关联性规则!

我们需要一个与优先级和关联性规则无关的规则,以解释子表达式的求值顺序。Java(和C#)中的相关规则是“子表达式从左到右求值”。由于A()出现在C()的左侧,因此无论C()参与乘法而A()仅涉及加法,都首先评估A()。

因此,现在你有足够的信息来回答你的问题。在a[b] = b = 0关联性规则中说这是a[b] = (b = 0);事实,但这并不意味着b=0先行!优先规则说索引比分配优先级更高,但这并不意味着索引器在最右边的分配之前运行。

(更新:此答案的较早版本在随后的部分中进行了一些小的且实际上不重要的遗漏,而我已对其进行了更正。我还在此撰写了一篇博客文章,描述了为什么这些规则在Java和C#中是明智的:https:// ericlippert.com/2019/01/18/indexer-error-cases/)

优先级和关联性仅告诉我们,to 的赋值b必须在 to的赋值之前发生a[b],因为零赋值会计算在索引操作中分配的值。优先级和是否关联性单独甭a[b]评估之前或之后的b=0。

同样,这与以下内容相同:A()[B()] = C()-我们所知道的是,索引必须在分配之前进行。我们不知道A(),B()或C()是基于优先级和关联性首先运行的。我们需要另一条规则来告诉我们。

规则再次是,“当你选择要做什么时,请始终从左到右”。但是,在这种特定情况下会有一个有趣的皱纹。由空集合或超出范围的索引引起的引发异常的副作用是否被视为分配左侧计算的一部分,还是分配本身计算的一部分?Java选择后者。(当然,这是一个区别,仅在代码已经错误的情况下才有意义,因为正确的代码不会取消引用null或首先传递错误的索引。)

那会怎样呢?

  • a[b]是的左侧b=0,所以a[b]跑第一,从而导致a[1]。但是,延迟检查此索引操作的有效性。
  • 然后b=0发生。
  • 然后发生a有效且a[1]在范围内的验证
  • a[1]最后要进行的值分配。

因此,尽管在这种特定情况下,对于那些本来不应该在正确的代码中发生的罕见错误情况,仍需要考虑一些细微之处,但是通常你可以推理:左边的事情先于右边的事情发生。这就是你要寻找的规则。关于优先权和关联性的讨论既令人困惑又无关紧要。

人们得到这个东西错了所有的时间,即使是人谁应该更清楚。我编辑了太多编程书籍,错误地陈述了规则,因此不足为奇的是,许多人对优先级/关联性和评估顺序之间的关系抱有完全错误的信念,也就是说,实际上没有这种关系; 他们是独立的。

2020-02-27