【Scala】單例物件與伴生物件
Scala的單例物件
Scala不能定義靜態成員,而是代之定義單例物件(singleton object)。以object關鍵字定義。
物件定義了某個類的單個例項,包含了你想要的特性:
object Accounts{
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1; lastNumber}
}
當你在應用程式中需要一個新的唯一賬號時,呼叫Account.newUniqueNumber()即可。
物件的構造器在該物件第一次被使用時呼叫。
在下面幾個場景下可以使用Scala單例物件:
- 作為存放工具函式或常量的地方
- 高效地共享單個不可變例項
- 需要使用單個例項來協調某個服務時
類和單例物件間的差別是,單例物件不帶引數,而類可以。因為單例物件不是用new關鍵字例項化的,所以沒機會傳遞給它例項化引數。每個單例物件都被實現為虛擬類(synthetic class)的例項,並指向靜態的變數,因為它們與Java靜態類有相同的初始化語義。
獨立物件(standalone object)
不與伴生類共享名稱的單例物件稱為獨立物件。它可以用在很多地方,例如作為相關功能方法的工具類,或者定義Scala應用的入口點。
伴生物件(companion object)
當單例物件與某個類共享同一個名稱時,它就被稱為是這個類的伴生物件(companion object)。類和它的伴生物件必須定義在同一個原始檔
class Account {
val id = Account.newUniqueNumber()
private var balance = 0.0
def deposit(amount: Double){ balance += amount }
...
}
object Account { //伴生物件
private var lastNumber = 0
def newUniqueNumber() = { lastNumber += 1 ; lastNumber}
}
注意:
- 類的伴生物件可以被訪問,但並不在作用域當中。Account類必須通過Account.newUniqueNumber()來呼叫伴生物件的方法。
- 在REPL中,要同時定義類和物件,必須用貼上模式。鍵入:paste
,然後鍵入或貼上類和物件的定義,最後一Ctrl+D退出貼上模式。
將伴生物件作為工廠使用
我們通常將伴生物件作為工廠使用。
下面是一個簡單的例子,可以不需要使用’new’來建立一個例項了。
class Bar(foo: String)
object Bar {
def apply(foo: String) = new Bar(foo)
}
繼承自類和特質的單例物件
一個object可以擴充套件類以及一個或多個特質,其結果是一個擴充套件了指定類以及特質的類的物件,同時擁有在物件定義中給出的所有特性。
繼承自抽象類的例子
擴充套件類的一個有用的使用場景是給出可被共享的預設物件。舉例來說,考慮在程式中引入一個可撤銷動作的類:
abstract class UndoableAction(val description: Sting) {
def undo(): Unit
def redo(): Unit
}
object DoNothingAction extends UndoableAction("Do nothing") {
override def undo() {}
override def redo() {}
}
//開啟和儲存功能尚未實現
val action = Map("open" -> DoNothingAction, "save" -> DoNothingAction, ...)
DoNothingAction物件可以被所有需要這個預設行為的地方共用
混入特質的例子
有時,你可以混入像debugger或logging之類的特質來構建物件幫助除錯物件,這樣使得構建的物件例項具有log之類的方法:
trait Debugger {
def log(message: String){
//do something with message
}
}
//no debugger
val child = new Child
//debugger added as the object is created
val problemChild = new ProblemChild with Debugger