转:http://houjp.com/2016/01/09/Scala%E8%BF%90%E7%AE%97%E7%AC%A6%E8%AF%A6%E8%A7%A3/
运算符的本质
在Scala中,真正的运算符只有直接赋值运算符(=),其他的的运算符其实都是方法(函数)。
val a = 1 + 2 // 等价于 1.+(2)
val b = 1 + 2 * 3 // 等价于 1.+(2.*(3))
运算符的重载
Scala中的运算符重载很简单,不需要多余的关键字,只要把运算符放在方法名的位置上就好了:
class MyClass(val x: Int) {
def +(o: MyClass) = new MyClass(x + o.x)
def *(o: MyClass) = new MyClass(x * o.x)
override def toString: String = {
x.toString
}
}
object MyClass {
def main(args: Array[String]) {
val o1 = new MyClass(2)
val o2 = new MyClass(3)
println(o1 + o2)
println(o1 * o2)
}
}
重载运算符可以是任意长度,但是只能由符号或者字母中的一类构成($和_算作字母)。比如<:<是合法的,但是
前缀运算符
前缀运算符只支持四种:~!+-,分别用unary_作为前缀来定义:
class MyClass(val x: Int) {
def unary_+ = new MyClass(x)
def unary_- = new MyClass(-x)
override def toString: String = {
x.toString
}
}
object MyClass {
def main(args: Array[String]) {
val o = new MyClass(2)
println(+o)
println(-o)
}
}
后缀运算符
后缀运算符支持并非默认开启,如果使用时不注意很容易编译不通过或者产生语义问题,实际中也较少使用到,所以在此不做过多说明。
简单的来说后缀操作符也是用户自定义的一些以unary_为开头的方法,之后直接使用在操作数的后面即可,默认是同其左边的操作数相结合的
运算符的调用方向
前缀运算符调用方向从右至左,后缀运算符从左至右。对于中缀运算符,Scala做了一个规定:以:结尾的运算符为从右至左调用,其他的运算符从左至右调用。
对于+:,以下表达式是等价的:
a +: b +: c
a +: c.+:(b)
c.+:(b).+:(a)
也就是说,从右至左调用的运算符会将右边的对象作为调用方法的对象,同时同等优先级下最右侧的运算符先执行,反之亦然。
运算符的优先级
运算符的优先级是由第一个字符的优先级决定的,键盘上可以输入,且不是语法要素的符号的优先级见下表:
优先级 | 运算符首字母 | 备注 |
---|---|---|
1 | ~,@,#,?,\ | Java中不会出现的符号 |
2 | *,/,% | 乘除法 |
3 | +,- | 加减法 |
4 | : | |
5 | <,> | 移位运算符和比较运算符 |
6 | !,= | 等于和不等于 |
7 | & | 与运算符 |
8 | ^ | 异或运算符 |
9 | | | 或运算符 |
10 | $,_,英文字母 | $和_是可以写入标识符的符号,和上面的符号有本质区别 |
特别注意以下几点:
- 前缀运算符高于所有中缀运算符。
- 以=结尾而又不以=开始的运算符会被当做赋值运算符而优先级低于以上所有运算符。
- 第一个字符相同的运算符优先级相同,从左至右依次执行(以:结尾的从右向左依次执行)。
对于第三点请参照以下程序及运行结果进行理解:
// 程序
class MyClass(val x: Int) {
def :*(o: MyClass) = new MyClass(x * o.x)
def :+(o: MyClass) = new MyClass(x + o.x)
override def toString: String = {
x.toString
}
}
object MyClass {
def main(args: Array[String]) {
val o1 = new MyClass(2)
val o2 = new MyClass(3)
val o3 = new MyClass(4)
println(o1 :+ o2 :* o3)
println(o1 :* o2 :+ o3)
}
}
// 运行结果
20
10