拟此篇以温习 Scala 对方法调用上的一些约定. 标题中说是关于操作符的事, 其实 Scala 像有了访问方法和属性的一致性原则一样, 可以说操作符与方法更是统一的, 或者说只有方法调用. 此处所称的操作符只不过是 Scala 对无参(prrameterless), 或只有一个参数的方法, 和特殊的四个 unary_+
, unary_-
, unary_!
, unary_~
方法的便捷的调用约定格式.
一. 中置操作符(对只有一个参数方法的调用约定, a plus b
)
case class MyNumber(value: Int) {
def +(that: MyNumber) = MyNumber(this.value + that.value)
}
调用方式
MyNumber(10).+(MyNumber(20)) //标准调用格式 MyNumber(10) + MyNumber(20) //只有一个参数时, 不用点, 不用括号
第一行是用点语法的标准方法调用格式, Scala 在当方法只有一个参数时, 可以省略点, 以及括号 , 因此可写为上面第二行种的格式. 所以方法 +
就化身为了中置操作符了.
+
只是一个普通的方法名而已, 因为 Scala 可以用很多符号来定义方法. 所以对于任何的只有一个参数的方法调用可以这么写, 假如 MyNumber
定义了方法
def plus(added: Int) = MyNumber(this.value + added)
应用同样的规则可写成
MyNumber(10) plus 20 //而可不必写成 MyNumber(10).plus(20)
这个约定十分有利于写出 DSL 风格的代码, 例如我们在测试中用 MustMatchers
时这么断言
actualList must have size 10
上面其实就是进行连续了三次是置操作符调用 size, have, 和 must.
除中置操作符外, 另外两种前置与后置操作符又统称为一元操作符, 因为它是不带参数的, 首先来看后置操作符
二. 后置操作符(对于无参数的方法的调用约定, a next
)
当函数无参数时, 调用时规则约定其实和只有一个参数时是一样的, 省略点以及括号, 既然一个参数都没有, 所以就只有方法名了. 例子
case class MyNumber(value: Int) { def -- = MyNumber(this.value -1) }
调用方式
MyNumber(10).-- //这是标准格式 MyNumber(10) -- //无参方法的约定格式
由于前面定义的 --
是无参方法(Parameterless), 所以调用时不能加括号, MyNumber(10).--()
是错误的. 如果是定义的空参数方法(Empty-Paren), 则既可以 MyNumber(10).--()
也可以写成 MyNumber(10) --
, 还能 MyNumber(10).--
, 但后两者会有警告.
def --() = MyNumber(this.value -1) //Empty-paren 方法 .... MyNumber(10) -- //Warning: Empty-paren method accessed as parameterless MyNumber(10).-- //Warning: Empty-paren method accessed as parameterless MyNumber(10).--()
所以对于后置操作符, 特别是无副作用时应该定义为不带括号的无参方法, 形如 def -- = ....
后置的调用方式很常见, 如
a toString //而不用写成 a.toString() 或 a.toString a mkString
Scala 可以实现在操作数前加 +
, -
, !
, ~
操作符, 如 -a
, 所以它们叫做前置(prefix) 操作符. Scala 的做法是把它们分别映射到方法 unary_+
, unary_-
, unary_!
, 和 unary_~
. 示例
case class MyNumber(value: Int) { def unary_+ = MyNumber(this.value + 1) }
调用时写成
MyNumber.unary_+ //标准调用格式, 或 MyNumber unary_+ +MyNumber(10) //实际就调用了 unary_+ 方法
注意, 前置操作只能映射前面四种操作, 想别的都没有. 比如你是否有冲动在 Scala 中实现出 ++a
呢? Scala 是不支持传统的 ++
递增操作, 后置的 a++
我们可以用 def ++ = ...
实现. 对于前置的 ++a
如果尝试写成
def unary_++ = MyNumber(this.value + 1) .... //++MyNumber(10) //这是无效的, 尝试不会是徒劳的. 只能显式的用下面两种方式调用 MyNumber(10).unary_++ MyNumber(10) unary_++
下面是测试上面三种方式的完整代码
import scala.language.postfixOps case class MyNumber(value: Int) { def +(that: MyNumber) = MyNumber(this.value + that.value) def ++ = MyNumber(this.value + 1) //parameterless // def -- = MyNumber(this.value - 1) //recommended def --() = MyNumber(this.value - 1) //Warning: Empty-paren, not recommended for no side-effect method def unary_- = MyNumber(-this.value) } object Main extends App { println("Infix: " + (MyNumber(10) + MyNumber(20))) //recommended println(MyNumber(10).+(MyNumber(20))) //regular method calling style println("Postfix1: " + (MyNumber(10) ++)) //recommended println(MyNumber(10).++) //regular method calling style // MyNumber(10).++() //Compilation error: Application does not take parameters println("Postfix2: " + (MyNumber(10) --)) //Warning: Empty-paren method accessed as parameterless println((MyNumber(10).--)) //Warning: Empty-paren method accessed as parameterless println(MyNumber(10).--()) //regular method calling style println("Prefix: " + -MyNumber(10)) println(MyNumber(10).unary_-) println((MyNumber(10) unary_-)) }
上面代码的输出
Infix: MyNumber(30)
MyNumber(30)
Postfix1: MyNumber(11)
MyNumber(11)
Postfix2: MyNumber(9)
MyNumber(9)
MyNumber(9)
Prefix: MyNumber(-10)
MyNumber(-10)
MyNumber(-10)