Scala 類和對象 (四)

2018-09-28 18:16 更新

類和對象 (四)

添加成員變量

本篇繼續(xù)上一篇,前面我們定義了 Rational 的主構造函數(shù),并檢查了輸入不允許分母為 0。下面我們就可以開始實行兩個 Rational 對象相加的操作。我們需要實現(xiàn)的函數(shù)化對象,因此 Rational 的加法操作應該是返回一個新的 Rational 對象,而不是返回被相加的對象本身。我們很可能寫出如下的實現(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)
}

實際上編譯器會給出如下編譯錯誤:

<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ù)的訪問范圍之內,但僅限于定義類的方法本身(比如之前定義的 toString 方法,可以直接訪問類參數(shù)),但對于 that 來說,無法使用 that.d 來訪問 d. 因為 that 不在定義的類可以訪問的范圍之內。此時需要定類的成員變量。(注:后面定義的 case class 類型編譯器自動把類參數(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 ,因為我們實現(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 來引用當前對象本身,一般來說訪問類成員時無需使用 this ,比如實現(xiàn)一個 lessThan 方法,下面兩個實現(xiàn)是等效的。

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

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

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

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

其中的 this 就無法省略。

輔助構造函數(shù)

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

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

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

私有成員變量和方法

Scala 類定義私有成員的方法也是使用 private 修飾符,為了實現(xiàn) Rational 的規(guī)范化顯示,我們需要使用一個求分子和分母的最大公倍數(shù)的私有方法 gcd。同時我們使用一個私有變量 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 的定義,因為它是個回溯函數(shù),必須定義返回值類型。Scala 會根據(jù)成員變量出現(xiàn)的順序依次初始化它們,因此g必須出現(xiàn)在 number 和 denom 之前。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號