定义运算符
本篇还将接着上篇Rational类,我们使用add定义两个Rational对象的加法。两个Rational加法可以写成
x.add(y)或者
x add y
即使使用x add y 还是没有 x + y 来得简洁。
我们前面说过在Scala中运算符(操作符)和普通的方法没有什么区别,任何方法都可以写成操作符的语法 比如 上面的 x add y.
而在Scala中对方法的名称也没有什么特别的限制,你可以使用符号作为类方法的名称,比如使用+,- * 等符号。因此我们可以重新定义Rational如下:
class Rational (n:Int, d:Int) { require(d!=0) private val g =gcd (n.abs,d.abs) val numer =n/g val denom =d/g override def toString = numer + "/" +denom def +(that:Rational) = new Rational( numer * that.denom + that.numer* denom, denom * that.denom ) def * (that:Rational) = new Rational( numer * that.numer, denom * that.denom) def this(n:Int) = this(n,1) private def gcd(a:Int,b:Int):Int = if(b==0) a else gcd(b, a % b) }
这样就可以使用 + ,* 号来实现Rational的加法和乘法。+,*的优先级是Scala预定的,和整数的+,-,* ,/的优先级一样。下面为使用Rational的例子:
scala> val x= new Rational(1,2) x: Rational = 1/2 scala> val y=new Rational(2,3) y: Rational = 2/3 scala> x+y res0: Rational = 7/6 scala> x+ x*y res1: Rational = 5/6
从这个例子也可以看出Scala语言的扩展性,你使用Rational对象就像Scala内置的数据类型一样。
Scala中的标识符
从前面的例子我们可以看到Scala可以使用两种形式的标志符,字符数字和符号。字符数字使用字母或是下划线开头,后面可以接字母或是数字,符号”$”在Scala中也看作为字母。然而以“$”开头的标识符为保留的Scala编译器产生的标志符使用,应用程序应该避免使用“$”开始的标识符,以免造成冲突。
Scala的命名规则采用和Java类似的camel命名规则,首字符小写,比如toString。类名的首字符还是使用大写。此外也应该避免使用以下划线结尾的标志符以避免冲突。
符号标志符包含一个或多个符号,如+,:,? 等,比如
+ ++ ::: < ?> :->
Scala 内部实现时会使用转义的标志符,比如:-> 使用$colon$minus$greater 来表示这个符号。因此如果你需要在Java代码中访问:->方法,你需要使用Scala的内部名称$colon$minus$greater。
混合标志符由字符数字标志符后面跟着一个或多个符号组成,比如 unary_+ 为Scala对+方法的内部实现时的名称。
字面量标志符为使用“定义的字符串,比如 x yield. 你可以在“之间使用任何有效的Scala标志符,Scala将它们解释为一个Scala标志符,一个典型的使用为 Thread的yield方法, 在Scala中你不能使用 Thread.yield()是因为yield为Scala中的关键字, 你必须使用 Thread.yield()来使用这个方法。
方法重载
和Java一样,Scala也支持方法重载,重载的方法参数类型不同而使用同样的方法名称,比如对于Rational对象,+的对象可以为另外一个Rational对象,也可以为一个Int对象,此时你可以重载+方法以支持和Int相加。
def + (i:Int) = new Rational (numer + i * denom, denom)
隐式类型转换
上面我们定义Rational的加法,并重载+以支持整数,r + 2 ,当如果我们需要 2 + r 如何呢? 下面的例子:
scala> val x =new Rational(2,3) x: Rational = 2/3 scala> val y = new Rational(3,7) y: Rational = 3/7 scala> val z = 4 z: Int = 4 scala> x + z res0: Rational = 14/3 scala> x + 3 res1: Rational = 11/3 scala> 3 + x <console>:10: error: overloaded method value + with alternatives: (x: Double)Double <and> (x: Float)Float <and> (x: Long)Long <and> (x: Int)Int <and> (x: Char)Int <and> (x: Short)Int <and> (x: Byte)Int <and> (x: String)String cannot be applied to (Rational) 3 + x ^
可以看到 x+3 没有问题,3 + x 就报错了,这是因为整数类型不支持和Rational相加。我们不可能去修改Int的定义(除非你重写Scala的Int定义)以支持Int和Rational相加。如果你写过.Net代码,这可以通过静态扩展方法来实现,Scala提供了类似的机制来解决这种问题。
如果Int类型能够根据需要自动转换为Rational类型,那么3 + x 就可以相加。Scala通过implicit def 定义一个隐含类型转换,比如定义由整数到Rational类型的转换如下:
implicit def intToRational(x:Int) = new Rational(x)
再重新计算r+2 和 2 + r的例子:
scala> val r = new Rational(2,3) r: Rational = 2/3 scala> r + 2 res0: Rational = 8/3 scala> 2 + r res1: Rational = 8/3
其实此时Rational的一个+重载方法是多余的, 当Scala计算 2 + r ,发现 2 (Int)类型没有 可以和 Rational对象相加的方法,Scala环境就检查Int的隐含类型转换方法是否有合适的类型转换方法,类型转换后的类型支持+ r,一检查发现定义了由Int到Rational的隐含转换方法,就自动调用该方法,把整数转换为Rational数据类型,然后调用Rational对象的+ 方法。从而实现了Rational类或是Int类的扩展。关于implicit def的详细介绍将由后面的文章来说明,隐含类型转换在设计Scala库时非常有用。
感觉本站内容不错,读后有收获?小额赞助,鼓励网站分享出更好的教程