我想编写一个可以很好地处理数字和操作符的类,所以我想知道当左参数是内置类型或其他无法修改实现的值时,如何重载操作符。
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
}
对于这个例子,下面的工作:
val c = new Complex(3,4)
c+2 == new Complex(5,4) //true
但是我也想写
2+c
不能作为Int.+
工作,不能接受Complex
类型的参数。有没有一种方法可以让我把它写成我想要的样子?
我已经找到了正确的关联方法,但它们似乎需要一个不同的操作符名称
可以添加从Int到Complex的隐式转换:
implicit def intToComplex(real: Int) = new Complex(real, 0)
你可以阅读有关隐式的内容(例如:理解Scala中的隐式)。简而言之,如果你的程序没有类型检查,那么当前作用域中的所有隐式都将被尝试,如果其中一些有效,编译器将应用它。
所以,当你写2 + c
时,编译器没有找到一个运算符+,它接受Int的复数,所以它尝试隐式。然后将2 + c
编译为intToComplex(2) + c
,这将正常工作。
使用前面提到的隐式转换将Int
转换为Complex
将完成这项工作。
这是一个有效的解决方案,它把所有这些放在一起,以补充Ivan的答案:
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")
}
}
一些注意事项:
- 在2.10.3下测试。根据您的版本,您可能需要在文件的顶部导入。
- 一旦你有隐式转换,你实际上不需要原始的+方法,它需要一个
Int
输入(因此我注释了它)。示例c4
证明了这一点。 - 考虑使你的类泛型-而不是使用
Double
,只是使用一些数字类型,这是足够广泛的,以满足您的需求。复数的概念实际上与它所扩展的域/环/群完全解耦。例如,如果您的底层类型具有乘法和加法,那么您也可以为复数定义乘法和加法。 - 我为
Tuple2[Double, Double]
类添加了一个隐式转换-只是因为它看起来很酷。示例c2
演示了它。 - 类似地,我添加了
apply
方法。考虑添加unapply
方法,使其对case
更友好。 - 我重命名了你的构造函数参数,只是为了使
toString
方法不那么混乱(所以你没有${I} I) - (侧轨)你的
==
方法是比较类型Double
,而不是Int
,所以你会得到所有通常的挫折和意想不到的行为与浮点比较
选自Scala参考资料6.2.3节
操作符的结合性由操作符的最后一个字符决定。以冒号":"结尾的操作符是右结合的。所有其他操作符都是左结合的。
所以最接近的是2 +: c