3.6 运算符重载

从技术的角度来看,Scala没有运算符,提及“运算符重载”时,指的是重载像++-等这样的符号。在Scala里,这些实际上是方法名:运算符利用了Scala灵活的方式调用语法——在Scala里,对象引用和方法名之间的点(.)不是必需的。

这两个特性给了我们运算符重载的错觉。这样,调用ref1 + ref2,实际上写的是ref1.+(ref2),这是在调用ref1.+()方法。看个+运算符的例子,来自Complex类,这个类表示复数⑥:

⑥复数有实部和虚部,它们对于计算复杂方程式很有用,比如负数的平方根。


ScalaForTheJavaEyes/Complex.scala

  1. class Complex(val real: Int, val imaginary: Int) {
  2. def +(operand: Complex) : Complex = {
  3. new Complex(real + operand.real, imaginary + operand.imaginary)
  4. }
  5. override def toString() : String = {
  6. real + (if (imaginary < 0) "" else "+") + imaginary + "i"
  7. }
  8. }
  9. val c1 = new Complex(1, 2)
  10. val c2 = new Complex(2, -3)
  11. val sum = c1 + c2
  12. println("(" + c1+ ")+ (" + c2 + ")=" + sum)

如果执行上面的代码会看到:

  1. (1+2i) + (2-3i) = 3-1i

在第一个语句中,创建了一个名为Complex的类,定义一个构造函数,接收两个参数。在4.1节中,我们会看到如何用Scala富有表现力的语法创建类。

+方法里创建了一个新的Complex类实例。结果的实部和虚部分别对应两个运算数实部和虚部的和。语句c1 + c2会变成一个方法调用,以c2为实参调用c1+()方法,也就是c1.+(c2)

我们讨论了Scala对运算符重载简单而优雅的支持。不过,Scala没有运算符,这个事实也许会让人有点头痛。或许,你会对运算符优先级感到困惑。Scala没有运算符,所以它无法定义运算符优先级,对吗?恐怕不是,因为24 - 2 + 3 * 6在Java和Scala里都等于40。Scala确实没有定义运算符优先级,但它定义了方法的优先级。

方法名的第一个字符决定了它的优先级⑦。如果表达式里有两个具有相同优先级的字符,那么左边的运算符优先级更高。下面从低到高列出了首字符的优先级⑧:

⑦如果方法名以冒号(:)结尾,Scala会调换方法调用的参数;参见8.4节,“方法名转换”。

⑧参见附录A。

  1. 所有字母
  2. |
  3. ^
  4. &
  5. < >
  6. = !
  7. :
  8. + -
  9. * / %
  10. 所有其他特殊字符

我们看个运算符/方法优先级的例子。下面的代码里,我们为Complex既定义了加法方法,又定义了乘法方法:

ScalaForTheJavaEyes/Complex2.scala

  1. class Complex(val real: Int, val imaginary: Int) {
  2. def +(operand: Complex) : Complex = {
  3. println("Calling +")
  4. new Complex(real + operand.real, imaginary + operand.imaginary)
  5. }
  6. def *(operand: Complex) : Complex = {
  7. println("Calling *")
  8. new Complex(real * operand.real - imaginary * operand.imaginary,
  9. real * operand.imaginary + imaginary * operand.real)
  10. }
  11. override def toString() : String = {
  12. real + (if (imaginary < 0) "" else "+") + imaginary + "i"
  13. }
  14. }
  15. val c1 = new Complex(1, 4)
  16. val c2 = new Complex(2, -3)
  17. val c3 = new Complex(2, 2)
  18. println(c1 + c2 * c3)

调用*()前,会先调用了在左边的+(),但是因为*()优先,它会先执行,如下所示:

  1. Calling *
  2. Calling +
  3. 11+2i