鍍金池/ 教程/ Scala/ 類和對(duì)象 (四)
包對(duì)象
Ordered Trait
組合和繼承–定義 final 成員
基本數(shù)據(jù)類型
Match 表達(dá)式
類和對(duì)象 (三)
操作基本數(shù)據(jù)類型
for 表達(dá)式
組合和繼承–重載成員函數(shù)和方法
類和對(duì)象 (二)
組合和繼承–定義 factory 對(duì)象
組合和繼承–多態(tài)和動(dòng)態(tài)綁定
Trait 的基本概念
if 表達(dá)式
組合和繼承–抽象類
函數(shù)–函數(shù)字面量的一些簡(jiǎn)化寫法
while 循環(huán)
組合和繼承–使用組合還是繼承?
訪問控制修飾符
Trait 示例–Rectangular 對(duì)象
組合和繼承–定義參數(shù)化成員變量
組合和繼承–定義無參數(shù)方法
類和對(duì)象 (一)
函數(shù)–閉包
函數(shù)–類成員函數(shù)
Scala 基本數(shù)據(jù)類型的實(shí)現(xiàn)方法
try 表達(dá)式處理異常
選擇瘦接口還是胖接口設(shè)計(jì)?
組合和繼承–小結(jié)
創(chuàng)建新的控制結(jié)構(gòu)
使用 import
為訪問控制修飾符添加作用域
Scala 的類層次關(guān)系
類和對(duì)象 (五)
傳名參數(shù)
柯里化函數(shù)
函數(shù)–頭等公民
組合和組合和繼承–定義 heighten 和 widen 函數(shù)
使用 Package–將代碼放入包中
隱含的 import
所有類的公共子類–底層類型
進(jìn)一步 Scala
函數(shù)–局部函數(shù)
引用包中的代碼
組合和繼承–使用 override 修飾符
組合和繼承–實(shí)現(xiàn)類 Element 的 above,beside 和 toString()方法
類和對(duì)象 (四)
函數(shù)–尾遞歸
沒有“break”和“continue”的日子
組合和繼承–調(diào)用基類構(gòu)造函數(shù)
減低代碼重復(fù)
函數(shù)–函數(shù)–可變參數(shù),命名參數(shù),缺省參數(shù)
起步 Scala
組合和繼承–擴(kuò)展類
函數(shù)–部分應(yīng)用的函數(shù)
開始神奇的 Scala編程之旅
組合和繼承–概述
Trait 用來實(shí)現(xiàn)可疊加的修改操作

類和對(duì)象 (四)

添加成員變量

本篇繼續(xù)上一篇,前面我們定義了 Rational 的主構(gòu)造函數(shù),并檢查了輸入不允許分母為 0。下面我們就可以開始實(shí)行兩個(gè) Rational 對(duì)象相加的操作。我們需要實(shí)現(xiàn)的函數(shù)化對(duì)象,因此 Rational 的加法操作應(yīng)該是返回一個(gè)新的 Rational 對(duì)象,而不是返回被相加的對(duì)象本身。我們很可能寫出如下的實(shí)現(xiàn):

class Rational (n:Int, d:Int) {
   require(d!=0)
   override def toString = n + "/" +d
   def add(that:Rational) : Rational =
     new Rational(n*that.d + that.n*d,d*that.d)
}

實(shí)際上編譯器會(huì)給出如下編譯錯(cuò)誤:

<console>:11: error: value d is not a member of Rational
            new Rational(n*that.d + that.n*d,d*that.d)
                                ^
<console>:11: error: value d is not a member of Rational
            new Rational(n*that.d + that.n*d,d*that.d)

這是為什么呢?盡管類參數(shù)在新定義的函數(shù)的訪問范圍之內(nèi),但僅限于定義類的方法本身(比如之前定義的 toString 方法,可以直接訪問類參數(shù)),但對(duì)于 that 來說,無法使用 that.d 來訪問 d. 因?yàn)?that 不在定義的類可以訪問的范圍之內(nèi)。此時(shí)需要定類的成員變量。(注:后面定義的 case class 類型編譯器自動(dòng)把類參數(shù)定義為類的屬性,這是可以使用 that.d 等來訪問類參數(shù))。

修改 Rational 定義,使用成員變量定義如下:

class Rational (n:Int, d:Int) {
   require(d!=0)
   val number =n
   val denom =d 
   override def toString = number + "/" +denom 
   def add(that:Rational)  =
     new Rational(
       number * that.denom + that.number* denom,
       denom * that.denom
     )
}

要注意的我們這里定義成員變量都使用了 val ,因?yàn)槲覀儗?shí)現(xiàn)的是“immutable”類型的類定義。number 和 denom 以及 add 都可以不定義類型,Scala 編譯能夠根據(jù)上下文推算出它們的類型。

scala> val oneHalf=new Rational(1,2)
oneHalf: Rational = 1/2
scala> val twoThirds=new Rational(2,3)
twoThirds: Rational = 2/3
scala> oneHalf add twoThirds
res0: Rational = 7/6
scala> oneHalf.number
res1: Int = 1

可以看到,這是就可以使用 .number 等來訪問類的成員變量。

自身引用

Scala 也使用 this 來引用當(dāng)前對(duì)象本身,一般來說訪問類成員時(shí)無需使用 this ,比如實(shí)現(xiàn)一個(gè) lessThan 方法,下面兩個(gè)實(shí)現(xiàn)是等效的。

def lessThan(that:Rational) =
   this.number * that.denom < that.number * this.denom

def lessThan(that:Rational) =
   number * that.denom < that.number * denom

但如果需要引用對(duì)象自身,this 就無法省略,比如下面實(shí)現(xiàn)一個(gè)返回兩個(gè) Rational 中比較大的一個(gè)值的一個(gè)實(shí)現(xiàn):

def max(that:Rational) =
      if(lessThan(that)) that else this

其中的 this 就無法省略。

輔助構(gòu)造函數(shù)

在定義類時(shí),很多時(shí)候需要定義多個(gè)構(gòu)造函數(shù),在 Scala 中,除主構(gòu)造函數(shù)之外的構(gòu)造函數(shù)都稱為輔助構(gòu)造函數(shù)(或是從構(gòu)造函數(shù)),比如對(duì)于 Rational 類來說,如果定義一個(gè)整數(shù),就沒有必要指明分母,此時(shí)只要整數(shù)本身就可以定義這個(gè)有理數(shù)。我們可以為 Rational 定義一個(gè)輔助構(gòu)造函數(shù),Scala 定義輔助構(gòu)造函數(shù)使用 this(…)的語法,所有輔助構(gòu)造函數(shù)名稱為 this。

def this(n:Int) = this(n,1)

所有 Scala 的輔助構(gòu)造函數(shù)的第一個(gè)語句都為調(diào)用其它構(gòu)造函數(shù),也就是 this(…),被調(diào)用的構(gòu)造函數(shù)可以是主構(gòu)造函數(shù)或是其它構(gòu)造函數(shù)(最終會(huì)調(diào)用主構(gòu)造函數(shù)),這樣使得每個(gè)構(gòu)造函數(shù)最終都會(huì)調(diào)用主構(gòu)造函數(shù),從而使得主構(gòu)造函數(shù)稱為創(chuàng)建類單一入口點(diǎn)。在 Scala 中也只有主構(gòu)造函數(shù)才能調(diào)用基類的構(gòu)造函數(shù),這種限制有它的優(yōu)點(diǎn),使得 Scala 構(gòu)造函數(shù)更加簡(jiǎn)潔和提高一致性。

私有成員變量和方法

Scala 類定義私有成員的方法也是使用 private 修飾符,為了實(shí)現(xiàn) Rational 的規(guī)范化顯示,我們需要使用一個(gè)求分子和分母的最大公倍數(shù)的私有方法 gcd。同時(shí)我們使用一個(gè)私有變量 g 來保存最大公倍數(shù),修改 Rational 的定義:

scala> class Rational (n:Int, d:Int) {
     |    require(d!=0)
     |    private val g =gcd (n.abs,d.abs) 
     |    val number =n/g 
     |    val denom =d/g 
     |    override def toString = number + "/" +denom
     |    def add(that:Rational)  = 
     |      new Rational( 
     |        number * that.denom + that.number* denom,
     |        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)
     | }
defined class Rational
scala> new Rational ( 66,42)
res0: Rational = 11/7

注意 gcd 的定義,因?yàn)樗莻€(gè)回溯函數(shù),必須定義返回值類型。Scala 會(huì)根據(jù)成員變量出現(xiàn)的順序依次初始化它們,因此g必須出現(xiàn)在 number 和 denom 之前。