學(xué)習(xí)Scala的重載方法和隱式轉(zhuǎn)換
方法重載
回到Rational類上來。在最近一次改變之后,你可以在分數(shù)上用自然的風(fēng)格做加法和乘法。但別忘了還有混合運算。例如,你不能把一個分數(shù)和一個整數(shù)乘在一起,因為‘*’的操作數(shù)只能是分數(shù)。所以對于分數(shù)r你不能寫r * 2。而必須寫成r * new Rational(2),看上去不漂亮。為了讓Rational用起來更方便,可以在類上增加能夠執(zhí)行分數(shù)和整數(shù)之間的加法和乘法的新方法。既然已經(jīng)到這里了,還可以再加上減法和除法。結(jié)果展示在代碼6.5中:
51CTO編輯推薦:Scala編程語言專題
代碼 6.5 含有重載方法的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
- def this(n: Int) = this(n, 1)
- def +(that: Rational): Rational =
- new Rational(
- numer * that.denom + that.numer * denom,
- denom * that.denom
- )
- def +(i: Int): Rational =
- new Rational(numer + i * denom, denom)
- def -(that: Rational): Rational =
- new Rational(
- numer * that.denom - that.numer * denom,
- denom * that.denom
- )
- def -(i: Int): Rational =
- new Rational(numer - i* denom, denom)
- def *(that: Rational): Rational =
- new Rational(numer * that.numer, denom * that.denom)
- def *(i: Int): Rational =
- new Rational(numer * i, denom)
- def /(that: Rational): Rational =
- new Rational(numer * that.denom, denom * that.numer)
- def /(i: Int): Rational =
- new Rational(numer, denom * i)
- override def toString = numer+"/"+denom
- private def gcd(a: Int, b: Int): Int =
- if (b == 0) a else gcd(b, a % b)
- }
現(xiàn)在每種數(shù)學(xué)方法都有兩個版本了:一個帶分數(shù)做參數(shù),另一個帶整數(shù)。或者可以說,這些方法名都被重載:overload了,因為每個名字現(xiàn)在都被多個方法使用。例如,+這個名字被一個帶Rational的和另一個帶Int的方法使用。方法調(diào)用里,編譯器會揀出正確地匹配了參數(shù)類型的重載方法版本。例如,如果x.+(y)的參數(shù)y是Rational,編譯器就會揀帶有Rational參數(shù)的+方法來用。相反如果參數(shù)是整數(shù),編譯器就會揀帶有Int參數(shù)的+方法做替代。如果你嘗試輸入:
- scala> val x = new Rational(2, 3)
- x: Rational = 2/3
- scala> x * x
- res37: Rational = 4/9
- scala> x * 2
- res38: Rational = 4/3
你會看到*方法的調(diào)用取決于每個例子里面右側(cè)操作數(shù)的類型。
注意
Scala分辨重載方法的過程與Java極為相似。任何情況下,被選中的重載版本都是***參數(shù)靜態(tài)類型的那個。有時如果不止一個***的版本;這種情況下編譯器會給你一個“參考模糊”的錯誤。
隱式轉(zhuǎn)換
現(xiàn)在你能寫r * 2了,或許你想交換操作數(shù),就像2 * r這樣。不幸的是這樣做還不可以:
這里的問題是2 * r等同于2.*(r),因此這是在整數(shù)2上的方法調(diào)用。但Int類沒有帶Rational參數(shù)的乘法——沒辦法,因為類Rational不是Scala庫的標準類。
- scala> 2 * r
- < console>:7: error: overloaded method value * with alternatives
- (Double)Double < and> (Float)Float < and> (Long)Long < and> (Int)Int
- < and> (Char)Int < and> (Short)Int < and> (Byte)Int cannot be
- applied to (Rational)
- val res2 = 2 * r
- ˆ
然而,Scala里有另外一種方法解決這個問題:你可以創(chuàng)建一個在需要的時候能自動把整數(shù)轉(zhuǎn)換為分數(shù)的隱式轉(zhuǎn)換。試著把這行代碼加入到解釋器:
這行代碼定義了從Int到Rational的轉(zhuǎn)換方法。方法前面的implicit修飾符告訴編譯器若干情況下自動調(diào)用它。定義了轉(zhuǎn)換之后,你現(xiàn)在可以重試之前失敗的例子了:
- scala> implicit def intToRational(x: Int) = new Rational(x)
請注意隱式轉(zhuǎn)換要起作用,需要定義在作用范圍之內(nèi)。如果你把隱式方法定義放在類Rational之內(nèi),它就不在解釋器的作用范圍?,F(xiàn)在,你要在解釋器內(nèi)直接定義它。
- scala> val r = new Rational(2,3)
- r: Rational = 2/3
- scala> 2 * r
- res0: Rational = 4/3
正如你在這個例子中能領(lǐng)略到的,隱式轉(zhuǎn)換是把庫變得更靈活和更方便的非常強大的技術(shù)。因為他們?nèi)绱藦姶螅砸埠苋菀妆徽`用。第二十一章里你將發(fā)現(xiàn)隱式轉(zhuǎn)換的更多細節(jié),包括在需要的時候把它們帶入作用范圍的方式。
【相關(guān)閱讀】
- Scala的四種標識符構(gòu)成方式
- Scala的私有字段和定義操作符
- Scala的從構(gòu)造器:主構(gòu)造器之外的構(gòu)造器
- 在Scala中檢查先決條件、添加字段和自指向
- Scala Rational對象的toString方法