方法命名约定

在3.6节“运算符重载”中,你看到了Scala是如何在没有运算符的情况下支持运算符重载的。运算符是一些遵循诡异方法命名约定的方法,方法名的第一个字符决定了优先级(参见3.6节“运算符重载”),而接下来,你会看到方法名的最后一个字符也有作用——它决定了方法调用的目标。

起初,:的约定可能会让人感到惊奇,但是,习惯了之后,(我喜欢把它叫做“开启Scala之眼”。)就会了解到,这会提升流畅性。比如,如果要在List前面添加一个值,可以写作value :: list。虽然它读作“在List前添加value”,但是方法的目标实际上是Listvalue是实参,也就是说list.::(value)

如果方法以冒号(:)结尾,则调用目标是运算符后面的实例④。在下一个例子里,^()Cow类所定义的一个方法,而^:()则是Moon类所定义的方法:

④Scala对方法名有规定,运算符不允许放在字母、数字这样的字符后面,除非这个运算符有下划线作为前缀。也就是说,方法不能叫jumpOver:(),而可以叫jumpOver_:()

ScalaIdioms/Colon.scala

  1. class Cow {
  2. def ^(moon: Moon) = println("Cow jumped over the moon")
  3. }
  4. class Moon {
  5. def ^:(cow: Cow) = println("This cow jumped over the moon too")
  6. }

下面是使用两个方法的例子:

ScalaIdioms/Colon.scala

  1. val cow = new Cow
  2. val moon = new Moon
  3. cow ^ moon
  4. cow ^: moon

上面代码里,两个方法调用看上去几乎一致的,cow在运算符左,moon在运算符右,不过,第一个是对cow的调用,而第二个是对moon的调用;差异之处非常细微。对于一些Scala新手而言,可能会很不习惯,但是这个约定在List运算中相当常见,你最好适应它。上面代码的输出如下:

  1. Cow jumped over the moon
  2. This cow jumped over the moon too

上面例子最后的调用也等价于这样的代码:

  1. moon.^:(cow)

除了以:结尾的运算符,还有一套运算符也是以其后的实例为目标的,包括一元运算符+-~。一元运算符+会映射成对unary_+()的调用,还有一元运算符-会映射到unary_-()等。

下面的例子里,Sample类定义了几个一元运算符:

ScalaIdioms/Unary.scala

  1. class Sample {
  2. def unary_+ = println("Called unary +")
  3. def unary_- = println("called unary -")
  4. def unary_! = println("called unary !")
  5. def unary_~ = println("called unary ~")
  6. }
  7. val sample = new Sample
  8. +sample
  9. -sample
  10. !sample
  11. ~sample

上面的代码输出如下:

  1. Called unary +
  2. called unary -
  3. called unary !
  4. called unary ~

随着对Scala的逐渐适应,你会开启Scala之眼——不久,对这些记法和约定的处理就会成为一种下意识行为。