1.Scala操作符简介
首先,请记住,Scala没有操作符!也没有通常意义上的表达式。你所见到的类似操作符和表达式的语句,其实是方法(函数),它们只是方法的一种比较直观的写法,可以叫做操作符记法。
1.1.二元操作符(中缀表达式)
二元操作符是最常见的操作符,比如,一个简单的表达式1 + 2
。其实,“+”是定义在Int类的一个方法,你完全可以用普通方法调用的写法1.+(2)
。相应的,其他的方法,比如"Hello".drop(2)
,也可以用操作符记法,"Hello" drop 2
。选择哪种书写方式,只需要考虑代码可读性,对编译器而言没有差别。
val sum1 = 1 + 2 //3 println("1 + 2 = " + sum1) val sum2 = (1).+(2) //3 println("(1).+(2) = " + sum2) val str1 = "Hello".drop(2) //"llo" println("\"Hello\".drop(2) = " + str1) val str2 = "Hello" drop 2 //"llo" println("\"Hello\" drop 2 = " + str2)
由上面代码可以看到,操作符跟方法没有区别。操作符与方法的统一,是Scala的一大特点。
1.2.一元操作符(前缀/后缀表达式)
能做前缀的操作符很少,只有四个:+,-,!,~。另外,前缀表达式有一些特别,它们的方法需要把对应的操作符前加上unary_,比如unary_!
,或者unary_-
。
println(-2.0) println((2.0).unary_-) println(true) println(false.unary_!)
请注意
除了上述四个前缀操作符外,即使定义其他的unary_method,这个方法也不会成为前缀操作符,比如你可以定义unary_*方法,你可以可以正常调用i.unary_*
,但是不能用在前缀表达式里*i
。
后缀表达式则没有上述限制。所有无参数的方法,都可以写作后缀表达式,比如i toString
相当于 i.toString()
1.3.操作符重载
由于操作符本质上就是方法,你可以跟方法重载一样重载操作符。C++与C#支持操作符重载,只是不像方法重载这样自然,而Java不允许操作符重载,因此,Scala的这种机制无疑有巨大优势。 比如,由于Java不支持操作符重载,对于表达式x * x * x
,当x是原生类型int时,可以这样写,但是如果x是BigInteger时,你只能写成x.multiply(x).multiply(x)
, 这就不怎么漂亮了。 在Scala中就没有这种问题,BigInt中定义的*
方法,可以直接这样写x * x * x
,或者,任意你自己定义的类,也可以定义类似的操作符。
另外,也是由于操作符就是方法,你可以定义任意你想要的操作符,比如BigInt类就有一个方法/%
,这个方法会返回一对数值,商和余数,与Java的BigInteger中的divideAndRemainder
方法类似。
val x: BigInt = 1003 println(x * x * x) //1009027027 println(x /% 5) //(200,3)
2.操作符的类型
操作符,大体上分为算术操作符,关系逻辑操作符和位操作符。
2.1.算术操作符
在Scala中,以中缀运算符的写法书写数学表达式,使用上与其他语言没有差别。 比如,1 + 2
, 2.5 - 5
, 3 * 4
, 6 / 4
, 10 % 3
, ‘A‘ - ‘a‘
。
区别在于,算术操作符也可以用于其他非简单类型的类中。
2.2.关系逻辑操作符
2.2.1.关系操作符
关系操作符,与其他大部分语言类似,比较两个对象,产生一个布尔型的结果。比如,1 > 2
, 3 < 4
, ‘a‘ <= ‘b‘
, 2.3 >= 2.3
, obj1 == obj2
请注意==
和!=
永远是比较两个对象的值,而不是像Java那样会比较对象的引用,这更符合人们的习惯。如果要比较对象引用,请使用对象的eq方法
val str1 = "abcd" val str2 = "abcd" println(str1 == str2) //true
2.2.2.逻辑操作符
逻辑操作符,逻辑与(&&
),逻辑或(||
),类似于Java等其他语言,由两个布尔操作数,产生一个布尔型的结果,比如true && false
, (1 < 2) || (2 > 3)
。
需要注意的是,与Java类似,逻辑操作符有短路(short-circuited)计算:只计算最少能决定结果的部分,也就是说,如果左侧的表达式已经能决定整个表达式的结果,右侧的表达式就不会计算。
我们都知道,true || (any statement)
的结果都是true,所以右侧表达式不会被计算。同样的,fasle && (any statement)
的结果都是false,右侧的表达式也不会被计算。
def trueSta = {println("evaluate 1 < 2 = true");1 < 2} def falseSta = {println("evaluate 2 > 3 = false"); 2 > 3} println(trueSta) println(falseSta) println("------------------------------") println("Short-circuit (true || anything ):") val ret = trueSta || falseSta println("------------------------------") println("Short-circuit (false && anything ):") val ret1 = falseSta && trueSta
2.3.位操作符
Scala给整数类型提供的位操作符有:按位与(&),按位或(|),按位异或(^),和按位取补操作。另外,还有三个位移操作:左移,右移和无符号右移。这些操作与其他语言相比没有特别之处,不过,由于很多程序员不习惯位操作,所以,我们来做几个练习。
println("1 & 2 = " + (1 & 2)) //1 & 2 = 0 println("1 | 2 = " + (1 | 2)) //1 | 2 = 3 println("2 ^ 3 = " + (2 ^ 3)) //2 ^ 3 = 1 println("~4 = " + (~4)) //~4 = -5 println("4 >> 1 = " + (4 >> 1)) //4 >> 1 = 2 println("4 << 2 = " + (4 << 2)) //4 << 2 = 16 println("-1 >> 31 = " + (-1 >> 31)) //-1 >> 31 = -1 println("-1 >>> 31 = " + (-1 >>> 31)) //-1 >>> 31 = -1
前面的四个表达式比较简单,1 & 2
也就是00001 & 0010
,产生的结果是0000
,也就是0,1 | 2
,就是0001 | 0010
,结果是0011
也就是3,0010 ^ 0011
,结果为0001
也就是1。 第四个需注意,取反是符号位也会取反的,因此,0000 0000 0000 0000 0000 0000 0000 0100
,取反的结果是1111 1111 1111 1111 1111 1111 1111 1011
,因此变成了一个负数,-5。
移位操作,尤其是左移或右移一位的操作,在实际编程中很实用,将一个整数右移(准确的说,无符号右移)一位,相当于将它除以2,而左移一位,则相当于乘以2。最后的两个表达式,帮助比较右移和无符号右移。
参考文献:
http://meetfp.com/zh/scala-basic/operators-ex