Java 無操作符重載導致 BigDecimal 運算極其冗長:
BigDecimal lhs = op1.add(op2.multiply(BigDecimal.valueOf(2))); BigDecimal rhs = op1.divide(op1.add(op2.multiply(BigDecimal.valueOf(1.5e2))), RoundingMode.HALF_UP); BigDecimal result = lhs.divide(rhs);
三大下一代 JVM 語言皆實現操作符重載,但方式各異。
Scala 消除操作符與方法區別,操作符只是特殊名稱的方法。* 為合法方法名(故導入用 _ 而非 *)。
final class Complex(val real: Int, val imaginary: Int) {
require(real != 0 || imaginary != 0)
def +(operand: Complex) = new Complex(real + operand.real, imaginary + operand.imaginary)
def +(operand: Int) = new Complex(real + operand, imaginary)
def -(operand: Complex) = new Complex(real - operand.real, imaginary - operand.imaginary)
def -(operand: Int) = new Complex(real - operand, imaginary)
def *(operand: Complex) = new Complex(
real * operand.real - imaginary * operand.imaginary,
real * operand.imaginary + imaginary * operand.real)
override def toString() = real + (if (imaginary < 0) "" else "+") + imaginary + "i"
override def equals(that: Any) = that match {
case other: Complex => (real == other.real) && (imaginary == other.imaginary)
case _ => false
}
override def hashCode(): Int = 41 * ((41 + real) + imaginary)
}
單元測試自然表達:
val c1 = new Complex(1, 3) val c2 = new Complex(4, 5) assert(c1 + c2 === new Complex(5, 8)) assert(c1 * c2 === new Complex(-11, 17))
Scala 預設左結合:x + y → x.+(y)。方法名以 : 結尾則右結合:i +: j → j.+:(i)。此設計支援開發者最大自由度。
5 + c1 會嘗試呼叫 Int.+() 失敗。解決方案:伴生物件定義隱式轉換:
object Complex {
implicit def intToComplex(x: Int) = new Complex(x, 0)
}
import Complex.intToComplex
優先級層次(高→低):特殊符號、* / %、+ -、:、= !、< >、&、^、|、字母、賦值操作符。以 = 結尾者最低優先級。
提供可重寫的映射方法:+ → plus()、- → minus()、* → multiply() 等。無法創建新操作符,但引入便利新操作符:?.(安全導航)、?:(Elvis 操作符,三元簡寫)。防止重載新操作符以保護可讀性。
操作符只是帶符號名稱的方法(如 +)。正確重載需理解協議與從公共內核生成方法群的技術(下期詳細介紹)。
三語言皆實現操作符重載,簡化 Java 繁瑣限制。Scala 以命名慣例處理關聯性/優先級,Groovy 映射現有操作符並引入新便利操作符,Clojure 以 Lisp 語法(全括號)消除歧義。下期探討「一切皆物件」在三語言中的深層含義。