JLS中f1()+ f2()* f3()表达式的执行顺序和运算符优先级

时间:2019-08-14 20:14:20

标签: java operator-precedence evaluation jls expression-evaluation

给出具有3个方法调用的表达式f1() + f2()*f3(),java首先对加法运算进行求值

int result = f1() + f2()*f3();

f1 working
f2 working
f3 working

我(错误地)期望首先调用f2(),然后调用f3(),最后调用f1()。因为乘法应在加法之前求值。

所以,我在这里不了解JLS-我想念什么?

  

15.7.3。 尊重评估括号和优先级

     

Java编程语言尊重评估顺序   由括号表示,由运算符暗指   优先级

在此示例中如何严格遵守运算符优先级?

JLS在15.7.5. Evaluation Order for Other Expressions中提到了一些异常(方法调用表达式(第15.12.4节),方法引用表达式(第15.13.3节)),但是我不能将这些异常中的任何一个应用于我的示例。

我了解JLS保证二进制运算的操作数从左到右求值。但是在我的示例中,为了理解方法调用顺序,有必要了解哪个操作(及其两个操作数!)首先被考虑。我在这里错了吗-为什么?

更多示例:

int result = f1() + f2()*f3() + f4() + f5()*f6() + (f7()+f8());

自豪地产生:

f1 working
f2 working
f3 working
f4 working
f5 working
f6 working
f7 working
f8 working

无论操作员的优先级如何,从左到右都是100%。

int result = f1() + f2() + f3()*f4();

产生:

f1 working
f2 working
f3 working
f4 working

此处,编译器有两个选项:

  1. f1()+f2()-首先添加
  2. f3()*f4()-首先乘法

但是“从左到右求值规则” 在这里很吸引人!

link意味着在此类示例中,无论任何括号和关联性规则如何,方法调用始终从左到右进行评估。

4 个答案:

答案 0 :(得分:10)

您丢失了15.7以下的文本:

  

Java编程语言保证运算符的操作数似乎按照特定的求值顺序(即从左到右)进行求值。

表达式操作数的在表达式本身之前进行求值。在您的特定情况下,最外面的表达式是f1() + [f2() * f3()]。我们必须首先评估f1(),然后必须评估f2() * f3()。其中,首先对f2()求值,然后对f3()求值,然后对乘法求值(这将返回+的操作数的值)。

答案 1 :(得分:6)

JLS §15.17.2. Evaluate Operands before Operation:

  

Java编程语言可确保   运算符(条件运算符&&||? :除外)   在操作
的任何部分本身之前进行充分评估   执行。

因此,f1()f2()f3()首先被评估(从左到右)。然后,应用运算符优先级。

毕竟,您可以在字节码中观察执行顺序,在我的情况下为:

[...]
INVOKESTATIC Main.f1 ()I
INVOKESTATIC Main.f2 ()I
INVOKESTATIC Main.f3 ()I
IMUL
IADD

这是表达式树的外观:

   +
  / \
 /   \
f1    *
     / \
    /   \
   f2   f3

首先评估叶子,然后在树本身中编码运算符优先级。

答案 2 :(得分:1)

  

因为乘法应在加法之前求值。

正如您似乎想说的那样,这不是对乘法优先于加法的合理描述,也不是第15.7.3节的效果的合理描述。优先表示您的表情

f1() + f2()*f3()

的评估结果与

相同
f1() + (f2() * f3())

,而不是相同的

(f1() + f2()) * f3()

无论操作数的性质如何,优先级都不会说明首先对+运算符的哪个操作数求值。而是说每个操作数。这确实影响了评估顺序,因为它遵循优先级规则,即原始表达式中的乘法结果必须在相加结果之前计算出来,但这并不能直接说明两个因子何时值的问题。相对于彼此或相对于其他子表达式进行评估。

与您引用的规则不同的规则负责计算+*运算符的两个操作数的顺序-始终是左操作数第一个,右操作数第二个。 / p>

  

在此示例中如何严格遵守运算符优先级?

该示例的输出未传达有关是否尊重优先级(正确理解)的信息。对于上面两个带括号的替代方案,将观察到相同的输出,并且优先级是这两个替代方案中的哪一个等同于原始表达式。

从操作顺序的意义上讲,优先级(仅)表示加法的右侧操作数为f2()*f3(),这意味着必须先计算乘积,然后才能计算总和。 JLS 15.7.3对此只字未提。子表达式是一个乘法,相对于加法运算的另一个操作数,它的求值顺序无关。

  

我了解JLS保证二进制运算的操作数从左到右进行评估。但是在我的示例中,   了解方法调用顺序有必要了解   考虑哪个操作(及其两个操作数!)   首先。我在这里错了吗-为什么?

是的,您错了,尽管有JLS,您的示例也提供了很好的证据。

问题似乎是“及其两个操作数”位。您的想法似乎大致是Java应该浏览表达式,找到所有的乘法运算并充分评估它们,包括它们的操作数,然后再返回,并对加法运算执行相同的操作。但这不是必须遵守优先顺序,并且与加法运算的从左到右的评估顺序不一致,这将使人类更难以编写Java代码。

让我们考虑一下您的较长示例:

int result = f1() + f2()*f3() + f4() + f5()*f6() + (f7()+f8());

评估过程如下:

  • f1()首先被求值,因为它是第一个操作的左操作数,并且该操作的操作数必须从左到右求值
  • f2()接下来将被求值,因为它是加法运算的右操作数的一部分(要对其进行运算),特别是乘法的左操作数
  • 接下来评估
  • f3(),因为正在评估它所属的乘法子表达式,并且它是该乘法的正确操作数。 优先级通过确定实际上正在评估的产品来影响此处的操作顺序 。如果不是这样,那么在计算f2()之前,此时将f1()的结果添加到f3()的结果中。
  • 根据乘法优先于加法计算f2()f3()的乘积。
  • 计算(先前评估的)f1()f2()*f3()的总和。这是由于*优先于已经讨论过的+,以及+的从左到右的结合性的组合,这确定了和是下一个加法的左操作数。操作,因此必须在该操作的正确操作数之前进行评估。
  • f4()会被求值,因为它是此时正在评估的加法运算的正确操作数。
  • 计算第二个和。再次是由于+的从左到右的关联性,以及 next 加法运算的从左到右的评估顺序。
  • f5(),然后f6(),然后评估其乘积。这完全类似于f2()*f3()的情况,因此是运算符优先级的另一种练习。
  • f5()*f6()的结果(作为加法运算的右侧操作数)被添加到左侧操作数。再次类似于先前的步骤。
  • f7()被评估。
  • 由于括号 ,接下来评估f8(),然后将其和与先前确定的f7()评估结果相加,并然后将那个结果添加到前面的结果中以完成计算。没有括号,顺序将有所不同:将f7()的结果添加到前面的结果中,然后计算f8(),然后添加该结果。

答案 3 :(得分:0)

在我看来,最有用的提示是discussion

enter image description here

JLS并不是很清楚-关于评估的第15.7节仅涉及单个操作的操作数,而不清楚的是现实生活中复杂的情况,其中有许多操作组合在一起。