方法命名约定
在3.6节“运算符重载”中,你看到了Scala是如何在没有运算符的情况下支持运算符重载的。运算符是一些遵循诡异方法命名约定的方法,方法名的第一个字符决定了优先级(参见3.6节“运算符重载”),而接下来,你会看到方法名的最后一个字符也有作用——它决定了方法调用的目标。
起初,:
的约定可能会让人感到惊奇,但是,习惯了之后,(我喜欢把它叫做“开启Scala之眼”。)就会了解到,这会提升流畅性。比如,如果要在List
前面添加一个值,可以写作value :: list
。虽然它读作“在List
前添加value
”,但是方法的目标实际上是List
,value
是实参,也就是说list.::(value)
。
如果方法以冒号(:
)结尾,则调用目标是运算符后面的实例④。在下一个例子里,^()
是Cow
类所定义的一个方法,而^:()
则是Moon
类所定义的方法:
④Scala对方法名有规定,运算符不允许放在字母、数字这样的字符后面,除非这个运算符有下划线作为前缀。也就是说,方法不能叫
jumpOver:()
,而可以叫jumpOver_:()
。ScalaIdioms/Colon.scala
class Cow {
def ^(moon: Moon) = println("Cow jumped over the moon")
}
class Moon {
def ^:(cow: Cow) = println("This cow jumped over the moon too")
}
下面是使用两个方法的例子:
ScalaIdioms/Colon.scala
val cow = new Cow
val moon = new Moon
cow ^ moon
cow ^: moon
上面代码里,两个方法调用看上去几乎一致的,cow
在运算符左,moon
在运算符右,不过,第一个是对cow
的调用,而第二个是对moon
的调用;差异之处非常细微。对于一些Scala新手而言,可能会很不习惯,但是这个约定在List
运算中相当常见,你最好适应它。上面代码的输出如下:
Cow jumped over the moon
This cow jumped over the moon too
上面例子最后的调用也等价于这样的代码:
moon.^:(cow)
除了以:
结尾的运算符,还有一套运算符也是以其后的实例为目标的,包括一元运算符+
、-
、!
和~
。一元运算符+
会映射成对unary_+()
的调用,还有一元运算符-
会映射到unary_-()
等。
下面的例子里,Sample
类定义了几个一元运算符:
ScalaIdioms/Unary.scala
class Sample {
def unary_+ = println("Called unary +")
def unary_- = println("called unary -")
def unary_! = println("called unary !")
def unary_~ = println("called unary ~")
}
val sample = new Sample
+sample
-sample
!sample
~sample
上面的代码输出如下:
Called unary +
called unary -
called unary !
called unary ~
随着对Scala的逐渐适应,你会开启Scala之眼——不久,对这些记法和约定的处理就会成为一种下意识行为。