Java 中求值顺序的规则是什么? [英] What are the rules for evaluation order in Java?

查看:41
本文介绍了Java 中求值顺序的规则是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am reading some Java text and got the following code:

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

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

In the text, the author did not give a clear explanation and the effect of the last line is: a[1] = 0;

我不太确定我是否理解:评估是如何进行的?

I am not so sure that I understand: how did the evaluation happen?

推荐答案

我说的很清楚,因为人们总是误解这一点:

Let me say this very clearly, because people misunderstand this all the time:

子表达式的求值顺序独立结合性和优先级.关联性和优先级决定了运算符的执行顺序,但决定了子表达式的计算顺序.您的问题是关于评估 子表达式 的顺序.

Order of evaluation of subexpressions is independent of both associativity and precedence. Associativity and precedence determine in what order the operators are executed but do not determine in what order the subexpressions are evaluated. Your question is about the order in which subexpressions are evaluated.

考虑A() + B() + C() * D().乘法优先于加法,加法是左结合的,所以这等价于 (A() + B()) + (C() * D()) 但知道这只告诉你知道第一次加法将在第二次加法之前发生,乘法将在第二次加法之前发生.它不会告诉您 A()、B()、C() 和 D() 将按什么顺序调用!(它也不会告诉您乘法发生在第一个之前还是之后另外.)完全有可能通过将其编译为以下内容来遵守优先级和结合性规则:

Consider A() + B() + C() * D(). Multiplication is higher precedence than addition, and addition is left-associative, so this is equivalent to (A() + B()) + (C() * D()) But knowing that only tells you that the first addition will happen before the second addition, and that the multiplication will happen before the second addition. It does not tell you in what order A(), B(), C() and D() will be called! (It also does not tell you whether the multiplication happens before or after the first addition.) It would be perfectly possible to obey the rules of precedence and associativity by compiling this as:

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(),并且仍然遵守优先级和结合性规则!

All the rules of precedence and associativity are followed there -- the first addition happens before the second addition, and the multiplication happens before the second addition. Clearly we can do the calls to A(), B(), C() and D() in any order and still obey the rules of precedence and associativity!

我们需要一个与优先级和结合性规则无关的规则来解释子表达式的求值顺序.Java(和 C#)中的相关规则是从左到右计算子表达式". 由于 A() 出现在 C() 的左侧,因此首先计算 A(),不管C() 涉及乘法而 A() 仅涉及加法.

We need a rule unrelated to the rules of precedence and associativity to explain the order in which the subexpressions are evaluated. The relevant rule in Java (and C#) is "subexpressions are evaluated left to right". Since A() appears to the left of C(), A() is evaluated first, regardless of the fact that C() is involved in a multiplication and A() is involved only in an addition.

所以现在您有足够的信息来回答您的问题.在 a[b] = b = 0 中,结合规则说这是 a[b] = (b = 0); 但这并不意味着 b=0 先运行!优先级规则说索引的优先级高于赋值,但是这并不意味着索引器在最右边的赋值之前运行.

So now you have enough information to answer your question. In a[b] = b = 0 the rules of associativity say that this is a[b] = (b = 0); but that does not mean that the b=0 runs first! The rules of precedence say that indexing is higher precedence than assignment, but that does not mean that the indexer runs before the rightmost assignment.

(更新:此答案的早期版本在接下来的部分中有一些小且实际上不重要的遗漏,我已更正.我还写了一篇博客文章,描述了为什么这些规则在 Java 和 C# 中是合理的:https://ericlippert.com/2019/01/18/indexer-error-cases/)

(UPDATE: An earlier version of this answer had some small and practically unimportant omissions in the section which follows which I have corrected. I've also written a blog article describing why these rules are sensible in Java and C# here: https://ericlippert.com/2019/01/18/indexer-error-cases/)

优先级和结合性只是告诉我们零的赋值b必须发生在之前a[b]的赋值,因为零的赋值计算在索引操作中赋值的值.优先级和结合性本身并没有说明 a[b] 是在 b=0 之前之前还是之后被评估.

Precedence and associativity only tell us that the assignment of zero to b must happen before the assignment to a[b], because the assignment of zero computes the value that is assigned in the indexing operation. Precedence and associativity alone say nothing about whether the a[b] is evaluated before or after the b=0.

同样,这与以下内容相同: A()[B()] = C() -- 我们所知道的是索引必须在赋值之前发生.根据优先级和结合性,我们不知道 A()、B() 或 C() 是先运行.我们需要另一条规则来告诉我们这一点.

Again, this is just the same as: A()[B()] = C() -- All we know is that the indexing has to happen before the assignment. We don't know whether A(), B(), or C() runs first based on precedence and associativity. We need another rule to tell us that.

同样,规则是当您可以选择先做什么时,请始终从左到右".然而,在这个特定场景中有一个有趣的问题.由空集合或超出范围的索引引起的抛出异常的副作用是被视为赋值左侧计算的一部分,还是赋值本身计算的一部分?Java 选择了后者.(当然,这只是一个区别,如果代码已经是错误的,因为正确的代码首先不会取消引用 null 或传递错误的索引.)

The rule is, again, "when you have a choice about what to do first, always go left to right". However, there is an interesting wrinkle in this specific scenario. Is the side effect of a thrown exception caused by a null collection or out-of-range index considered part of the computation of the left side of the assignment, or part of the computation of the assignment itself? Java chooses the latter. (Of course, this is a distinction that only matters if the code is already wrong, because correct code does not dereference null or pass a bad index in the first place.)

那么会发生什么?

  • a[b]b=0 的左边,所以 a[b]运行,结果为 a[1].但是,检查此索引操作的有效性会延迟.
  • 然后 b=0 发生.
  • 然后验证a有效且a[1]在范围内
  • 将值分配给 a[1] 最后发生.
  • The a[b] is to the left of the b=0, so the a[b] runs first, resulting in a[1]. However, checking the validity of this indexing operation is delayed.
  • Then the b=0 happens.
  • Then the verification that a is valid and a[1] is in range happens
  • The assignment of the value to a[1] happens last.

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

So, though in this specific case there are some subtleties to consider for those rare error cases that should not be occurring in correct code in the first place, in general you can reason: things to the left happen before things to the right. That's the rule you're looking for. Talk of precedence and associativity is both confusing and irrelevant.

人们总是把这些东西弄错,即使是那些本该知道得更好的人.我编辑了太多的编程书籍,它们错误地说明了规则,因此很多人对优先级/结合性和评估顺序之间的关系有完全错误的信念也就不足为奇了——即,实际上不存在这样的关系;他们是独立的.

People get this stuff wrong all the time, even people who should know better. I have edited far too many programming books that stated the rules incorrectly, so it is no surprise that lots of people have completely incorrect beliefs about the relationship between precedence/associativity, and evaluation order -- namely, that in reality there is no such relationship; they are independent.

如果您对这个主题感兴趣,请参阅我关于该主题的文章以进一步阅读:

If this topic interests you, see my articles on the subject for further reading:

http://blogs.msdn.com/b/ericlippert/archive/tags/precedence/

它们是关于 C# 的,但其中大部分内容同样适用于 Java.

They are about C#, but most of this stuff applies equally well to Java.

这篇关于Java 中求值顺序的规则是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆