Scala编程(第5版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.9 操作符优先级和结合律

操作符优先级决定了表达式中的哪些部分会先于其他部分被求值。例如,表达式2 + 2 * 7经求值得到16而不是28,因为操作符*的优先级高于+。因此,表达式的乘法部分先于加法部分被求值。当然,也可以在表达式中用圆括号来澄清求值顺序,或者覆盖默认的优先级。例如,如果想要上述表达式经求值得到28,则可以像这样来写:

Scala并不是真的有操作符,操作符仅仅是用操作符表示法使用方法的一种方式。你可能会好奇操作符优先级的工作原理是什么。Scala根据操作符表示法中使用的方法名的首个字母来判定优先级(这个规则有一个例外,会在后面讲到)。举例来说,如果方法名以*开头,它将拥有比以+开头的方法更高的优先级。因此2 + 2 * 7会被当作2 + (2 * 7)求值。同理,a +++ b *** c(其中abc是变量,+++***是方法)将被当作a +++ (b *** c)求值,因为***方法比+++方法的优先级更高。

表5.3显示了方法首字符的优先级顺序,且依次递减,位于同一行的拥有同样的优先级。

表5.3 操作符优先级

在表格中某个字符的优先级越高,则以这个字符开头的方法就拥有更高的优先级。如下例子展示了优先级的影响:

<<方法以字符<开头,在表5.3中,<出现在字符+的下方,因此表达式会先调用+方法,再调用<<方法,即2 << (2 + 2)。按数学方法计算,2 + 242 << 432。如果将这两个操作交换一下次序,将会得到不同的结果:

由于方法的首字符与前一例一样,方法将会按照相同的顺序调用。先是+方法,再是<<方法。因此2 + 24,而4 << 216

前面提到过,优先级规则的一个例外是赋值操作符(assignment operator)。这些操作符以等号(=)结尾,且不是比较操作符(<=>===!=),它们的优先级与简单的赋值(=)拥有的优先级一样。也就是说,比其他任何操作符都低。例如:

与如下代码是一样的:

因为*=被归类为赋值操作符,而赋值操作符的优先级比+低,尽管它的首字符是*,看上去应该比+的优先级更高。

当多个同等优先级的操作符并排在一起时,操作符的结合律决定了操作符的分组。Scala中操作符的结合律由操作符的最后一个字符决定。正如我们在第3章提到的,任何以':'字符结尾的方法都是在它右侧的操作元上调用,并传入左侧的操作元的。以任何其他字符结尾的方法则相反:这些方法是在左侧的操作元上调用,并传入右侧的操作元的。因此a * b交出a.*(b),而a ::: b将交出b.:::(a)

不过,无论操作符的结合律是哪一种,它的操作元都是从左到右被求值的。因此,如果a不是一个简单的引用某个不可变值的表达式,则更准确地说,a ::: b会被当作如下的代码块:

在这个代码块中,a仍然是先于b被求值的,然后这个求值结果被作为操作元传入b:::方法。

这个结合律规则在相同优先级的操作符并排出现时也有相应的作用。如果方法名以':'结尾,它们会被从右向左依次分组;否则,它们会被从左向右依次分组。例如,a ::: b ::: c 被当作a ::: (b ::: c),而a * b * c则被当作(a * b) * c

操作符优先级是Scala语言的一部分,在使用时不需要过于担心。话虽如此,一个好的编码风格可以清晰地表达出什么操作符被用在什么表达式上。也许你可以唯一真正放心让其他程序员能够不查文档就能知道的优先级规则是,乘法类的操作符(*/%)比加法类的操作符(+-)拥有更高的优先级。因此,虽然a + b << c在不加任何圆括号的情况下可以交出你想要的结果,但是把表达式写成(a + b) << c会带来额外的清晰效果,也可能会减少别人用操作符表示法对你表达不满的频率,比如,愤懑地大声说这是“bills !*&^%~ code!”。[11]