2010-11-11 111 views
11

我仍然在學習Scala,但我認爲有趣的一件事是Scala模糊了方法和字段之間的界限。舉例來說,我可以建立像這樣一類...Scala屬性問題

class MutableNumber(var value: Int) 

這裏的關鍵是,在構造函數參數的自動無功讓我用「值」字段像在Java中的getter/setter。

// require all mutable numbers to be >= 0 
class MutableNumber(private var _value: Int) { 
    require(_value >= 0) 

    def value: Int = _value 
    def value_=(other: Int) { 
     require(other >=0) 
     _value = other 
    } 
} 

客戶端代碼不破,因爲沒有按API:

// use number... 
val num = new MutableNumber(5) 
num.value = 6 
println(num.value) 

如果我想補充的限制,我可以通過切換到位實例字段的使用方法,這樣做不會改變:

// use number... 
val num = new MutableNumber(5) 
num.value = 6 
println(num.value) 

我的掛機是添加到Scala-2.8的命名參數功能。如果我使用命名參數,我的API 更改,並且它確實打破api。

val num = new MutableNumber(value=5) // old API 
val num = new MutableNumber(_value=5) // new API 

num.value = 6 
println(num.value) 

有沒有優雅的解決方案呢?我應該如何設計我的MutableNumber類,以便稍後添加約束而不破壞API?

謝謝!

回答

11

您可以使用與案例類相同的技巧:使用隨播對象。

object Example { 
    class MutableNumber private (private var _value: Int) { 
    require (_value >= 0) 
    def value: Int = _value 
    def value_=(i: Int) { require (i>=0); _value = i } 
    override def toString = "mutable " + _value 
    } 
    object MutableNumber { 
    def apply(value: Int = 0) = new MutableNumber(value) 
    } 
} 

在這裏,這是工作(和證明,作爲構建,您必須使用對象的創作,因爲構造函數被標記爲私有):

scala> new Example.MutableNumber(5) 
<console>:10: error: constructor MutableNumber cannot be accessed in object $iw 
    new Example.MutableNumber(5) 
^

scala> Example.MutableNumber(value = 2) 
res0: Example.MutableNumber = mutable 2 

scala> Example.MutableNumber() 
res1: Example.MutableNumber = mutable 0 
+0

有趣內的任何類的所有成員!所以通過隱藏構造函數,我強制每個人都使用伴侶對象。如果我想讓MutableInteger本身成爲一個案例類呢?我知道如果我只是在類定義之前放置'case',Scala會自動爲我創建伴隨對象...這個解決方案是否仍然有效? – shj 2010-11-11 20:44:24

+0

是的,如果你有一個明確定義的伴隨對象的case類,那麼該對象的成員將被合併到生成的對象中(並且可以覆蓋,如果你想提供一個調整的'Companion.apply()'方法但保留自動生成的'unapply')。 – 2010-11-11 20:47:24

+0

@shj - 不,這不起作用,因爲案例類別假定對構造函數變量進行直接(非保護)訪問。你這樣做是因爲你需要守衛(在這種情況下以'require(_value> = 0)'的形式)。 – 2010-11-11 20:48:34

2

感謝您的回答!順便說一句,我認爲斯卡拉-人可能知道,有一個問題:

什麼用Scala 2.8的新功能:命名和默認參數

... 直到現在,參數的名字是這對圖書館開發人員來說是一種隨意的選擇,並且不被視爲API的重要組成部分。這突然改變了,所以如果參數sep在更高版本中被重命名爲分隔符,那麼調用mkString(sep =「」)的方法將無法編譯。

Scala 2.9爲這個問題提供了一個很好的解決方案,但是在我們等待的時候,如果他們的名字在將來可能會改變,請謹慎地引用名稱參數。

+0

斯卡拉2.9的整潔解決方案是什麼? – vossad01 2012-12-15 23:48:43

2
class MutableNumber { 
    private var _value = 0 //needs to be initialized 
    def value: Int = _value 
    def value_=(other: Int) { 
     require(other >=0) //this requirement was two times there 
     _value = other 
    } 
} 

可以修改大括號

val n = new MutableNumber{value = 17} 
+0

這樣做的缺點是爲每個使用花括號的MutableNumber實例化創建一個匿名。 – 2010-11-15 01:50:40