Scalaでも演算子のオーバーロードをしたい

はじめに

Scala演算子オーバーロードについてサクッと確認してみます。

オーバーロード

まぁ、とりあえず言語仕様を見ておけば安心だよねといった感じです。困ったら公式ドキュメントを読む。日本書紀にもそう書いてあります。

case class AltInt(val value: Int) {
  def +(x: AltInt) = plus(x)
  def plus(x: AltInt) = AltInt(value + x.value)
  def unary_~ = AltInt(value * -1)
  def +:(x: Int) = AltInt(value + x)
  def -:(x: AltInt) = { println(s"$value - ${x.value} = ${value - x.value}"); AltInt(value - x.value) }
  def *(x: AltInt) = AltInt(x.value * value)
  def ! = println(value)
}

中置演算子

Scalaでは引数を一つ取るメソッドはすべて中置演算子として扱えます。 逆に+のような演算子も普通*1のメソッドのように呼び出せます。

val a = AltInt(10)
val b = AltInt(20)

println(a.plus(b))
println(a plus b)
println(a.+(b))
println(a + b)

なお、優先順位はメソッドの先頭の文字によって規定されるようです。

先頭の文字 優先度
(all letters) 低い
|
^
&
= !
< >
:
+ -
* / %
(all other special characters) 高い
val a = AltInt(10)
val b = AltInt(20)
println(a * a + b) // 120

また、メソッドが:で終わる場合は引数とレシーバの位置関係が逆転します。

println(1 +: a)

また、通常演算子は左結合ですがこの場合は右結合になります。

println(b -: a -: a)
10 - 10 = 0
0 - 20 = -20
AltInt(-20)

後置演算子

中置演算子と同じように引数を取らないメソッドは後置演算子として呼び出せます。

println(a!)
println(a.!)

前置演算子

前置演算子unary_記号で定義できますが、記号に入ることができるのは+-!~に限られます。

println(~a)
println(a.unary_~)

おわりに

素直でいいですね。

*1:普通?普通ってなんだ?