1. 程式人生 > >【Scala】單例物件與伴生物件

【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)。類和它的伴生物件必須定義在同一個原始檔

中。類被稱為是這個單例物件的伴生類(companion class)。類和它的伴生物件可以互相訪問其私有成員

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