2016-12-14 85 views
1

我創建了一個類Door並且想寫一個函數,將打開狀態改變爲任何參數。Scala函數改變屬性的類

以下不起作用。這樣做的最好方法是什麼? (對不起,對於初學者問題。)

class Door(var name: String, 
      var open: Boolean){ 
     def open_door(newDoorState: Boolean): Unit = new Door(name, newDoorState) 
     override def toString(): String = 
"(" + name + ", " + newDoorState + ")" 

} 

var door = new Door("MyDoor",true) 
door.name // "MyDoor" 
door.open // true 

door.open_door(false) 
door.name // "MyDoor" 
door.open // still true. I would like this to be false 

回答

2

隨着盧卡斯和Venkat Sudheer雷迪Aedama在他們的評論中提到,在斯卡拉變異值的推薦的方法是使用不可變對象。

這意味着每次更改對象狀態時,您都將創建一個具有更新值的新對象。實現參照透明度是必要的,以允許程序免於副作用,這被認爲是令代碼更容易維護的理想屬性。

在Scala中有一個稱爲case類的簡便機制。讓我們來看看Door是如何被定義爲一個案例類:

case class Door(name: String, open: Boolean) { 
    def opened(open: Boolean) = copy(open = open) 
} 

正如你可能已經注意到有是類參數之前沒有varval。這是因爲如果每個參數都被假定爲val,那麼默認情況下,case類是不可變的。

另一個區別是,它不使用new運算符創建新實例,而是使用copy方法,該方法允許更改指定字段的值。可能很難看出現在這樣做的好處,但當您處理更多字段的案例類時,它變得非常方便。現在

您可以實例Door並以下列方式更新:

val door = Door("MyDoor", false) 
door.name // "MyDoor" 
door.open // false 

val door2 = door.opened(true) 
door2.name // "MyDoor" 
door2.open // true 

的情況下,類提供的另一個方便之處在於,你可以實例化它們不使用new關鍵字,它是調用Door.apply("MyDoor", false)一個語法糖。

到目前爲止,我們已經看到了如何爲不可變數據對象聲明和使用一個case類。但這是否意味着我們應該總是這樣宣佈我們的班級?

案例類的重要屬性是它們是透明的。這是由於他們的所有領域都暴露爲公衆val s。當你想以與存儲對象相同的方式讀取對象的屬性時,這很有用。另一方面,如果你不想暴露你的對象的內部(所以你的類可以被稱爲不透明),例如爲了強制一些不變量,使用標準的Scala類可能是一個更好的主意。

對於與透明度和不透明度相關的一般規則和權衡,我推薦閱讀李浩一的文章Strategic Scala Style: Designing Datatypes


在某些情況下,例如,如果你想提高性能或降低系統的內存佔用,你可能會決定使用代替可變類:

class Door(var name: String, 
      var open: Boolean) { 
    def openDoor(newDoorState: Boolean): Unit = 
    open = newDoorState 
} 

val door = new Door("MyDoor",true) 
door.name // "MyDoor" 
door.open // true 

door.openDoor(false) 
door.name // "MyDoor" 
door.open // false 

只需記住的是,不可變對象通常是傳遞數據的首選方式,您應該將程序設計爲在默認情況下是透明透明的,並且當它變得非常必要時,請考慮諸如將數據結構更改爲可變的微觀優化。

另外我會補充說,在斯卡拉有一個約定(源於Java)使用駱駝案例的類字段和方法,所以,而不是命名您的方法open_door像原來的例子,你應該命名它openDoor

+2

這將是值得一提的是OP應該使用'方法名稱camelCase',指出,通常最好使用第一種變種,如果他想創建一個新的對象,可能會創建一個'case class Door(name:String,open:Boolean)'而不是''copy''來實現'opened'方法。 –

+1

如@盧卡斯如上所述: '情況下類門(名稱:字符串,ISOPEN:布爾值){ DEF執行Opendoor(newDoorState:布爾型):門= this.copy(ISOPEN = newDoorState) }' –

+1

盧卡斯,Venkat Sudheer雷迪Aedama對於遲到的答覆我很抱歉,這確實是一個懶惰的答案。感謝您的建議,我現在更詳細地回答了我的答案。 – adamwy

1

你的open_door函數返回一個新的門。相反,它應該設置open屬性:

class Door(var name: String, var open: Boolean) { 
    def openDoor(newDoorState: Boolean) { 
    open = newDoorState 
    } 
    override def toString(): String = "(" + name + ", " + newDoorState + ")" 
} 
0

您當前的open_door方法創建Door的新實例,其中open設置爲newDoorState。

達到你想要什麼,你要做的:

def open_door(newDoorState: Boolean): Unit { 
    open = newDoorState 
} 

(注意等號的函數體之前,由於缺少此外,您還可以刪除「:單位」的一部分 - 這將是推斷。 )

+3

使用'='是必需的。過程語法幾年前已棄用。 https://issues.scala-lang.org/browse/SI-7605 – retrospectacus

+0

我明白了,謝謝你的注意。一年前,我仍然使用Scala版本,它是有效的。 – Ashalynd

0

謝謝,迄今爲止答案都是面向對象的。 我添加一個功能更強大的答案,即不改變原有門類:

class Door{ 
     var name="MyDoor" 
     var open_status=true 
def this(name:String, open_status:Boolean){ 
    this() 
    this.open_status = open_status 
} 

override def toString = { 
"%s is open is %s.".format(name, open_status) 
} 
} 

println(new Door) 
println(new Door("MyDoor",false))