给出具有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
此处,编译器有两个选项:
f1()+f2()
-首先添加f3()*f4()
-首先乘法但是“从左到右求值规则” 在这里很吸引人!
此link意味着在此类示例中,无论任何括号和关联性规则如何,方法调用始终从左到右进行评估。
答案 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)