2017-03-09 64 views
4

我來自Java背景,也是新的Scala,目前正在閱讀「Scala編程」一書。Scala:類參數訪問vs對象字段訪問

有書中如下的例子:

class Rational(n: Int, d: Int) { // This won't compile 
    require(d != 0) 
    override def toString = n + "/" + d 

    def add(that: Rational): Rational = new Rational(n * that.d + that.n * d, d * that.d) 
} 

然而,鑑於此代碼的編譯器抱怨:

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

解釋說:

儘管類參數n和d在您的add方法的代碼範圍內,您只能在調用add的對象上訪問它們的值。因此,當你在add的實現中說n或d時,編譯器很樂意爲你提供這些類參數的值。但它不會讓你說.n或that.d,因爲它沒有引用添加被調用的Rational對象。要訪問分子和分母,你需要將它們放入字段中。

也是一個正確的執行,給出如下:

class Rational(n: Int, d: Int) { 
    require(d != 0) 
    val numer: Int = n 
    val denom: Int = d 

    override def toString = numer + "/" + denom 

    def add(that: Rational): Rational = 
    new Rational(
     numer * that.denom + that.numer * denom, 
     denom * that.denom 
    ) 
} 

我試圖瞭解過很多次,但目前尚不清楚。

我已經在課堂上有nd參數。我可以通過add方法訪問它們。我將另一個Rational對象傳遞給add方法。它也應該有nd,對吧?

that.nthat.d有什麼問題?爲什麼我需要在字段中使用參數?

此外,重寫的toString方法只是簡單地採取nd,這不會失敗嗎?

我可能聽起來很愚蠢,但在我前進之前,確實需要明確理解這一點,以獲得更好的基本面。

回答

5

傳遞給類構造函數的參數默認爲私有成員,因此可用於所有類代碼(如toString覆蓋中所示),但它們不能作爲實例成員訪問(因此that.d不起作用)。

您可以告訴編譯器不要使用默認值。

class Rational(val n: Int, val d: Int) { // now it compiles 
    ... 

或者,傳遞給case class的參數默認爲實例成員。

case class Rational(n: Int, d: Int) { // this also compiles 
    ... 
2

Scala擁有比Java更多類型的訪問修飾符。在斯卡拉有一個叫做private[this]的東西,意思是「對當前對象是私有的」,它比正常的private更嚴格,這意味着「這個類的所有對象都是私有的」。

class Rational(n: Int, d: Int) 

是基本相同

class Rational(private[this] val n: Int, private[this] val d: Int) 

在更高,幾乎是哲學層面上可以說,

class Rational(n: Int, d: Int) { ... } 

就像是一個靜態方法返回一個Rational,它有參數nd,並且如同在任何方法中它的參數對於該方法是局部的。通過使用valvar來限定參數,可以將這些參數轉換爲Rational的字段,而不必將它們寫兩次:一次作爲方法的參數(或構造函數,這是此靜態方法的更具體的名稱),一次作爲一個領域。