操作符的优先级决定了表达式的哪个部分先于其他部分被评估。举例来说,表达式2 + 2 * 7计算得16,而不是28,因为*操作符比+操作符有更高的优先级。因此表达式的乘法部分先于加法部分被评估。当然你还可以在表达式里使用括号来厘清评估次序或覆盖优先级。例如,如果你实际上希望上面表达式的值是28,你可以这么写表达式:
51CTO编辑推荐:Scala编程语言专题
- (2 + 2) * 7
由于Scala没有操作符,实际上,是以操作符的格式使用方法的一个途径,你或许想知道操作符优先级是怎么做到的。Scala基于操作符格式里方法的第一个字符决定优先级(这个规则有一个例外,稍后再说)。比方说,如果方法名开始于*,那么就比开始于+的方法有更高的优先级。因此2 + 2 * 7将被评估为2 + (2 * 7),而a +++ b *** c(这里a,b和c是值或变量,而+++和***是方法)将被看作是a +++ (b *** c),因为***方法比+++方法有更高的优先级。
表格 5.3 操作符优先级
表格5.3以降序方式展示了根据方法第一个字符指定的优先级,同一行的字符具有同样的优先级。表格中字符的位置越高,以这个字符开始的方法具有的优先级就越高。举例如下:
<<方法开始于字符<,在表格5.3里的位置比+(+方法的第一个也是唯一的一个字符)要低。因此<<比+的优先级低,表达式也要在先调用了+方法之后再调用<<方法,如2 << (2 + 2)。我们可以算一下,2 + 2得4,2 << 4得32。下面给出另一个例子:
- scala> 2 << 2 + 2
- res41: Int = 32
由于第一个字符与前面的例子里一样,因此调用的方法顺序也没有不同。首先+方法被调用,然后是<<方法。因此2 + 2得4,4 << 2得16。
- scala> 2 + 2 << 2
- res42: Int = 16
上面提到的优先级规则的一个例外,有关于以等号结束的赋值操作符:assignment operator。如果操作符以等号字符(=)结束,且操作符并非比较操作符<=,>=,==,或=,那么这个操作符的优先级与赋值符(=)相同。也就是说,它比任何其他操作符的优先级都低。例如:
与下面的相同:
- x *= y + 1
因为*=被当作赋值操作符,它的优先级低于+,尽管操作符的第一个字符是*,似乎其优先级高于+。
- x *= (y + 1)
当同样优先级的多个操作符肩并肩地出现在表达式里,操作符的关联性:associativity决定了操作符分组的方式。Scala里操作符的关联性取决于它的最后一个字符。正如第3章里47页提到的,任何以‘:’字符结尾的方法由它的右手侧操作数调用,并传入左操作数。以其他字符结尾的方法有其他的说法。它们都是被左操作数调用,并传入右操作数。因此a * b变成a.*(b),但是a:::b变成b.:::(a)。然而,不论操作符具有什么样的关联性,它的操作数总是从左到右评估的。因此如果b是一个表达式而不仅仅是一个不可变值的指针的话,那么更精确的意义上说,a:::b将会当作是:
- { val x = a; b.:::(x) }
这个代码块中,a仍然在b之前被评估,然后评估结果被当作操作数传给b的:::方法。
这种关联性规则在同时使用多个具有同优先级的操作符时也会起作用。如果方法结束于:,它们就被自右向左分组;反过来,就是自左向右分组。例如,a ::: b ::: c会被当作a ::: (b ::: c)。而a * b * c被当作(a * b) * c。
操作符优先级也是Scala语言的一部分。你不用怕它。但无论如何,使用括号去厘清什么操作符作用在哪个表达式上都是好的风格。或许你唯一可以确信其他人不用查书就知道的优先级关系就是乘除法操作符(*,/和%),比加减法(+和-)的要高。因此即使a + b << c不用括号也能产生你想要的结果,写成(a + b) << c而得到的简洁性也可能会减少你的同事为了表示不满在操作符注释里写你名字的频率,“bills!*&^%~code!” 到目前为止,你应该能指出给出的这段代码,Scala编译器会调用成(bills.!*&^%~(code)).!()。
【相关阅读】