学习Scala的重载方法和隐式转换

开发 后端
本文节选自Martin Odersky,Lex Spoon和Bill Venners所著,Regular翻译的《Programming in Scala》的第六章。Scala是一种针对 JVM 将函数和面向对象技术组合在一起的编程语言。

方法重载

回到Rational类上来。在最近一次改变之后,你可以在分数上用自然的风格做加法和乘法。但别忘了还有混合运算。例如,你不能把一个分数和一个整数乘在一起,因为‘*’的操作数只能是分数。所以对于分数r你不能写r * 2。而必须写成r * new Rational(2),看上去不漂亮。为了让Rational用起来更方便,可以在类上增加能够执行分数和整数之间的加法和乘法的新方法。既然已经到这里了,还可以再加上减法和除法。结果展示在代码6.5中:

51CTO编辑推荐:Scala编程语言专题

  1. class Rational(n: Int, d: Int) {  
  2.  require(d != 0)  
  3.  private val g = gcd(n.abs, d.abs)  
  4.  val numer = n / g  
  5.  val denom = d / g  
  6.  def this(n: Int) = this(n, 1)  
  7.  def +(that: Rational): Rational =  
  8.   new Rational(  
  9.    numer * that.denom + that.numer * denom,  
  10.    denom * that.denom  
  11.   )  
  12.  def +(i: Int): Rational =  
  13.   new Rational(numer + i * denom, denom)  
  14.  def -(that: Rational): Rational =  
  15.   new Rational(  
  16.    numer * that.denom - that.numer * denom,  
  17.    denom * that.denom  
  18.   )  
  19.  def -(i: Int): Rational =  
  20.   new Rational(numer - i* denom, denom)  
  21.  def *(that: Rational): Rational =  
  22.   new Rational(numer * that.numer, denom * that.denom)  
  23.  def *(i: Int): Rational =  
  24.   new Rational(numer * i, denom)  
  25.  def /(that: Rational): Rational =  
  26.   new Rational(numer * that.denom, denom * that.numer)  
  27.  def /(i: Int): Rational =  
  28.   new Rational(numer, denom * i)  
  29.  override def toString = numer+"/"+denom  
  30.  private def gcd(a: Int, b: Int): Int =  
  31.   if (b == 0) a else gcd(b, a % b)  
  32. }  
代码 6.5 含有重载方法的Rational

现在每种数学方法都有两个版本了:一个带分数做参数,另一个带整数。或者可以说,这些方法名都被重载:overload了,因为每个名字现在都被多个方法使用。例如,+这个名字被一个带Rational的和另一个带Int的方法使用。方法调用里,编译器会拣出正确地匹配了参数类型的重载方法版本。例如,如果x.+(y)的参数y是Rational,编译器就会拣带有Rational参数的+方法来用。相反如果参数是整数,编译器就会拣带有Int参数的+方法做替代。如果你尝试输入:

  1. scala> val x = new Rational(23)  
  2. x: Rational = 2/3 
  3. scala> x * x  
  4. res37: Rational = 4/9 
  5. scala> x * 2 
  6. res38: Rational = 4/3 

你会看到*方法的调用取决于每个例子里面右侧操作数的类型。

注意

Scala分辨重载方法的过程与Java极为相似。任何情况下,被选中的重载版本都是***参数静态类型的那个。有时如果不止一个***的版本;这种情况下编译器会给你一个“参考模糊”的错误。

隐式转换

现在你能写r * 2了,或许你想交换操作数,就像2 * r这样。不幸的是这样做还不可以:

  1. scala> 2 * r  
  2. < console>:7: error: overloaded method value * with alternatives  
  3. (Double)Double < and> (Float)Float < and> (Long)Long < and> (Int)Int  
  4. < and> (Char)Int < and> (Short)Int < and> (Byte)Int cannot be  
  5. applied to (Rational)  
  6.   val res2 = 2 * r  
  7.                  ˆ  
这里的问题是2 * r等同于2.*(r),因此这是在整数2上的方法调用。但Int类没有带Rational参数的乘法——没办法,因为类Rational不是Scala库的标准类。

然而,Scala里有另外一种方法解决这个问题:你可以创建一个在需要的时候能自动把整数转换为分数的隐式转换。试着把这行代码加入到解释器:

  1. scala> implicit def intToRational(x: Int) = new Rational(x)  
这行代码定义了从Int到Rational的转换方法。方法前面的implicit修饰符告诉编译器若干情况下自动调用它。定义了转换之后,你现在可以重试之前失败的例子了:

  1. scala> val r = new Rational(2,3)  
  2. r: Rational = 2/3 
  3. scala> 2 * r  
  4. res0: Rational = 4/3 
请注意隐式转换要起作用,需要定义在作用范围之内。如果你把隐式方法定义放在类Rational之内,它就不在解释器的作用范围。现在,你要在解释器内直接定义它。

正如你在这个例子中能领略到的,隐式转换是把库变得更灵活和更方便的非常强大的技术。因为他们如此强大,所以也很容易被误用。第二十一章里你将发现隐式转换的更多细节,包括在需要的时候把它们带入作用范围的方式。

【相关阅读】

  1. Scala的四种标识符构成方式
  2. Scala的私有字段和定义操作符
  3. Scala的从构造器:主构造器之外的构造器
  4. 在Scala中检查先决条件、添加字段和自指向
  5. Scala Rational对象的toString方法

责任编辑:book05 来源: Artima
相关推荐

2009-09-04 10:49:19

C#隐式转换

2009-07-22 08:34:47

Scala方法和字段

2009-07-20 16:56:51

Scala类的定义

2023-12-12 08:50:22

MySQL隐式转换varchar

2022-02-23 21:24:21

索引SQL字符

2009-08-27 10:06:15

Scala的构造方法

2023-08-14 08:35:36

2010-02-04 16:47:04

C++隐式转换

2017-12-20 14:14:16

数据库MySQL数据类型

2023-09-22 09:04:00

C++编程

2009-08-05 14:09:04

C#日期转换

2009-07-21 17:26:09

Scala本地函数

2023-04-27 09:08:19

JavaScript隐式类型转换

2017-09-13 10:58:51

JavaScript转换规则

2021-12-01 06:03:24

JS TrueJavaScript

2017-09-05 16:17:35

JavaScript运算转换

2010-11-16 15:11:52

Oracle隐式游标

2009-07-08 16:10:24

Scala简介面向对象函数式

2009-09-27 15:29:00

Scala讲座面向对象Scala

2010-07-19 09:40:59

SQL Server数
点赞
收藏

51CTO技术栈公众号