2013-01-04 70 views
1

來自java背景我總是將實例變量標記爲私有。我正在學習scala,並且幾乎所有我查看過的代碼中的val/var實例都具有默認(公共)訪問權限。爲什麼這是訪問?它不會破壞信息隱藏/封裝原則嗎?爲什麼不將val或var變量標記爲私有?

回答

7

這將幫助您指定其中代碼,但請記住,某些示例代碼是以簡化形式突出顯示示例應該顯示給您的內容。由於默認訪問是公開的,這意味着爲了簡單起見,您經常會忽略修飾符。

這就是說,由於val是不可改變的,有沒有在留公衆多大的危害只要你認識到這是現在你類的API的一部分。這可以是完全沒問題:

class DataThingy(data: Array[Double) { 
    val sum = data.sum 
} 

或者,它可以是一個實現細節,你不應該暴露:

class Statistics(data: Array[Double]) { 
    val sum = data.sum 
    val sumOfSquares = data.map(x => x*x).sum 
    val expectationSquared = (sum * sum)/(data.length*data.length) 
    val expectationOfSquare = sumOfSquares/data.length 
    val varianceOfSample = expectationOfSquare - expectationSquared 
    val standardDeviation = math.sqrt(data.length*varianceOfSample/(data.length-1)) 
} 

在這裏,我們已經充斥我們班所有的中間步驟計算標準偏差。這是非常愚蠢的,因爲這不是用數字計算標準偏差最具數字穩定性的方法。

而不是僅讓這些私人的,它是更好的風格,如果可能的話,使用本地塊或private[this] DEFS執行中間計算:

val sum = data.sum 
val standardDeviation = { 
    val sumOfSquares = ... 
    ... 
    math.sqrt(...) 
} 

val sum = data.sum 
private[this] def findSdFromSquares(s: Double, ssq: Double) = { ... } 
val standardDeviation = findMySD(sum, data.map(x => x*x).sum) 

如果你需要存儲一個計算以供以後使用,那麼private valprivate[this] val是要走的路,但如果它只是計算的中間步驟,則上述選項更好。

同樣,如果它是接口的一部分(如可變向量上的向量座標),則公開var並沒有什麼壞處。但是,當它是一個實現細節時,你應該使它們成爲private(更好的是:private[this],如果可以的話!)。

+6

您對隱藏區塊中的中間步驟有何看法?這將很難在評論系統中格式化,但基本上val standardDeviation = {val sumOfSquares = ...; val expectationSquared = ...; var varianceOfSample = ...; math.sqrt(data.length * varianceOfSample /(data.length -1))} – joev

+0

@joev - 這就是做事的好方法 - 往往更好,其實比離開私有變量。沒有辦法在他們完成之後讓他們坐在一起佔用空間。 –

+0

@Rex Kerr'如果它是接口的一部分,暴露var就沒有什麼壞處 - 當我們使用接口的一部分時,我們不應該讓'var'-s是volatile嗎? – idonnie

2

實際上,一些Scala開發者傾向於使用默認訪問。但是你可以在着名的Scala項目中找到合適的例子(例如Twitter的Finagle)。

另一方面,將對象創建爲不可變值是Scala中的標準方法。如果完全不可變,我們不需要隱藏所有的屬性。

+0

[*只是一個音符*]事實上,'val'不保證完整的不變性。對於它只是確保對該對象的引用是不可變的。 –

3

Java和Scala之間的一個重要區別是,在Java中,您不能用getter和setter方法(反之亦然)替換公共變量而不破壞源和二進制兼容性。在斯卡拉你可以。

所以在Java中,如果你有一個公共變量,它是一個變量的事實將暴露給用戶,如果你改變它,用戶必須改變他的代碼。在Scala中,您可以使用getter和setter方法(或僅使用getter方法的公共val)替換公開的var,而用戶不知道其差異。所以從這個意義上說,沒有實現細節暴露。


舉個例子,讓我們考慮一個矩形類:

class Rectangle(val width: Int, val height:Int) { 
    val area = width * height 
} 

如果我們稍後決定,我們不希望區域存儲爲一個變量現在發生了什麼,而是應該每次被調用時計算一次?

在Java中的情況會是這樣的:如果我們使用一個getter方法和一個私有變量,我們可以只取出變量和更改getter方法來計算面積,而不是使用的變量。不需要更改用戶代碼。但由於我們使用的公共變量,我們現在不得不打破用戶代碼:-(

在Scala中是不同的:。我們只能改變valdef,這就是它沒有更改需要用戶代碼

2

我想回答有一點更通用的方法的問題。我認爲你正在尋找的答案與上Scala是建立在設計範例做,而是經典的prodecural /面向對象的方法,如你看在Java中,函數式編程用於高得多的延伸。我不能涵蓋所有你提到課程的代碼,但一般(寫得很好)Scala代碼並不需要很多的可變性。

正如雷克斯指出的那樣,val是不可改變的,所以他們不公開的原因很少。但正如我所看到的那樣,不變性本身不是一個目標,而是函數式編程的結果。所以如果我們認爲功能像x -> function -> y那樣function部分變得有點黑盒子;我們並不在意它的作用,只要它能正確地做。由於Haskell Wiki寫道:

純功能程序通常對不可變數據進行操作。與其更改現有值,不創建更改的副本並保留原始文件。

這也解釋了缺失的閉合,因爲我們傳統上想要隱藏的部分在函數中執行,因此無論如何都隱藏了。

所以,切東西短,我認爲可變性和封閉Scala中變得更加多餘。爲什麼在可以避免的情況下與吸氣劑和二次吸盤混淆?

相關問題