I want to write a class which can work nicely with numbers and operators, so I want to know how to overload an operator when the left hand argument is a built-in type or other value for which I can't modify the implementation.
class Complex(val r:Double,val i:Double){
def +(other:Complex) = new Complex(r+other.r,i+other.i)
def +(other:Double) = new Complex(r+other,i)
def ==(other:Complex) = r==other.r && i==other.i
}
With this example, the following works:
val c = new Complex(3,4)
c+2 == new Complex(5,4) //true
But I also want to be able to write
2+c
which doesn't work as Int.+ can't accept an argument of type Complex.
Is there a way to let me write this and have it work as I want?
I've found out about right associative methods, but they seem to require a different operator name.
You can add an implicit conversion from Int to Complex:
implicit def intToComplex(real: Int) = new Complex(real, 0)
You can read about implicits (e.g. here: Understanding implicit in Scala). The short version is that, if your program doesn't type check, all implicits that are in the current scope will be tried and if some of them works, it will be applied by the compiler.
So, when you write 2 + c, the compiler doesn't find an operator + that takes a complex in Int, so it tries the implicits. It will then compile 2 + c to intToComplex(2) + c which will work correctly.
Using implicit conversions to turn Int into Complex as mentioned before will do the job.
Here's a working solution which puts it all together to complement Ivan's answer:
import scala.language.implicitConversions
class Complex(val real:Double, val imaginary:Double){
def +(other:Complex) = new Complex(real+other.real, imaginary+other.imaginary)
//def +(other:Double) = new Complex(real+other,imaginary) // Not needed now
def ==(other:Complex) = real==other.real && imaginary==other.imaginary
override def toString: String = s"$real + ${imaginary}i"
}
object Complex {
implicit def intToComplex(real: Int): Complex = doubleToComplex(real.toDouble)
implicit def doubleToComplex(real: Double): Complex = Complex(real, 0)
implicit def apply(real: Double, imaginary: Double): Complex = new Complex(real, imaginary)
implicit def apply(tuple: (Double, Double)): Complex = Complex(tuple._1, tuple._2)
def main(args: Array[String]) {
val c1 = Complex(1, 2)
println(s"c1: $c1")
val c2: Complex = (3.4, 4.2) // Implicitly convert a 2-tuple
println(s"c2: $c2")
val c3 = 2 + c1
println(s"c3: $c3")
val c4 = c1 + 2 // The 2 is implicitly converted then complex addition is used
println(s"c4: $c4")
}
}
Some notes:
Int input (hence I commented it out). Example c4 proves this. Double, just use some numerical type which is broad enough for your needs. The concept of a complex number is actually completely decoupled from the field/ring/group it extends. For example, if your underlying type has multiplication and addition, you can define multiplication and addition for complex numbers too.Tuple2[Double, Double] class - just because it seemed cool. Example c2 demonstrates it.apply methods. Consider adding unapply methods to make it more case friendly.toString method less confusing (so you don't have ${i}i)== method is comparing type Double, not Int, so you'll get all the usual frustration and unexpected behaviour with floating point comparisonsIf you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With