Scala 類和對象 (五)

2018-09-28 18:16 更新

類和對象 (五)

定義運(yùn)算符

本篇還將接著上篇 Rational 類,我們使用 add 定義兩個(gè) Rational 對象的加法。兩個(gè) Rational 加法可以寫成 x.add(y)或者 x add y 即使使用 x add y 還是沒有 x + y 來得簡潔。

我們前面說過在 Scala 中運(yùn)算符(操作符)和普通的方法沒有什么區(qū)別,任何方法都可以寫成操作符的語法。比如:上面的 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)
}

這樣就可以使用 + , 號來實(shí)現(xiàn) Rational 的加法和乘法。+,*的優(yōu)先級是 Scala 預(yù)定的,和整數(shù)的+,-, ,/的優(yōu)先級一樣。下面為使用 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

從這個(gè)例子也可以看出 Scala 語言的擴(kuò)展性,你使用 Rational 對象就像 Scala 內(nèi)置的數(shù)據(jù)類型一樣。

Scala中的標(biāo)識符

從前面的例子我們可以看到 Scala 可以使用兩種形式的標(biāo)志符,字符數(shù)字和符號。字符數(shù)字使用字母或是下劃線開頭,后面可以接字母或是數(shù)字,符號”$”在 Scala 中也看作為字母。然而以“$”開頭的標(biāo)識符為保留的 Scala 編譯器產(chǎn)生的標(biāo)志符使用,應(yīng)用程序應(yīng)該避免使用“$”開始的標(biāo)識符,以免造成沖突。

Scala 的命名規(guī)則采用和 Java 類似的 camel 命名規(guī)則,首字符小寫,比如 toString。類名的首字符還是使用大寫。此外也應(yīng)該避免使用以下劃線結(jié)尾的標(biāo)志符以避免沖突。符號標(biāo)志符包含一個(gè)或多個(gè)符號,如+,:,? 等,比如:

+ ++ ::: < ?> :->

Scala 內(nèi)部實(shí)現(xiàn)時(shí)會(huì)使用轉(zhuǎn)義的標(biāo)志符,比如:-> 使用 $colon$minus$greater 來表示這個(gè)符號。因此如果你需要在 Java 代碼中訪問:->方法,你需要使用 Scala 的內(nèi)部名稱 $colon$minus$greater。

混合標(biāo)志符由字符數(shù)字標(biāo)志符后面跟著一個(gè)或多個(gè)符號組成,比如 unary_+ 為 Scala 對+方法的內(nèi)部實(shí)現(xiàn)時(shí)的名稱。字面量標(biāo)志符為使用“定義的字符串,比如 `x` `yield`。你可以在“之間使用任何有效的 Scala 標(biāo)志符,Scala 將它們解釋為一個(gè) Scala 標(biāo)志符,一個(gè)典型的使用為 Thread 的 yield 方法, 在 Scala 中你不能使用 Thread.yield()是因?yàn)?yield 為 Scala 中的關(guān)鍵字, 你必須使用 Thread.`yield`()來使用這個(gè)方法。

方法重載

和 Java 一樣,Scala 也支持方法重載,重載的方法參數(shù)類型不同而使用同樣的方法名稱,比如對于 Rational 對象,+的對象可以為另外一個(gè) Rational 對象,也可以為一個(gè) Int 對象,此時(shí)你可以重載+方法以支持和 Int 相加。

def + (i:Int) =
     new Rational (numer + i * denom, denom)

隱式類型轉(zhuǎn)換

上面我們定義 Rational 的加法,并重載+以支持整數(shù),r + 2 ,當(dāng)如果我們需要 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 就報(bào)錯(cuò)了,這是因?yàn)檎麛?shù)類型不支持和 Rational 相加。我們不可能去修改 Int 的定義(除非你重寫 Scala 的 Int 定義)以支持 Int 和 Rational 相加。如果你寫過 .Net 代碼,這可以通過靜態(tài)擴(kuò)展方法來實(shí)現(xiàn),Scala 提供了類似的機(jī)制來解決這種問題。如果 Int 類型能夠根據(jù)需要自動(dòng)轉(zhuǎn)換為 Rational 類型,那么 3 + x 就可以相加。Scala 通過 implicit def 定義一個(gè)隱含類型轉(zhuǎn)換,比如定義由整數(shù)到 Rational 類型的轉(zhuǎn)換如下:

implicit def intToRational(x:Int) = new Rational(x)

再重新計(jì)算 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

其實(shí)此時(shí) Rational 的一個(gè)+重載方法是多余的, 當(dāng) Scala 計(jì)算 2+ r,發(fā)現(xiàn) 2(Int)類型沒有 可以和 Rational 對象相加的方法,Scala 環(huán)境就檢查 Int 的隱含類型轉(zhuǎn)換方法是否有合適的類型轉(zhuǎn)換方法,類型轉(zhuǎn)換后的類型支持+r,一檢查發(fā)現(xiàn)定義了由 Int 到 Rational 的隱含轉(zhuǎn)換方法,就自動(dòng)調(diào)用該方法,把整數(shù)轉(zhuǎn)換為 Rational 數(shù)據(jù)類型,然后調(diào)用 Rational 對象的+ 方法。從而實(shí)現(xiàn)了 Rational 類或是 Int 類的擴(kuò)展。關(guān)于 implicit def 的詳細(xì)介紹將由后面的文章來說明,隱含類型轉(zhuǎn)換在設(shè)計(jì) Scala 庫時(shí)非常有用。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號