Scala的自定義類型標記

Scala的自定義類型標記ide

Scala中有不少千奇百怪的符號標記,看起來是那麼的獨特,就像是一杯dry martini...好像黑夜中的螢火蟲,那麼耀眼,那麼出衆。this

好了言歸正傳,這一篇文章咱們會講一下Scala中的自定義類型標記,經過自定義類型標記能夠將this指向額外的類型指望。scala

咱們先看一個觀察者模式的例子:code

abstract class SubjectObserver {                                     
  type S <: Subject    // <1>                                              
  type O <: Observer   // <2>

  trait Subject {                                                    
    private var observers = List[O]()  // <3>

    def addObserver(observer: O) = observers ::= observer

    def notifyObservers() = observers.foreach(_.receiveUpdate(this)) // <4>
  }

  trait Observer {                                                   
    def receiveUpdate(subject: S) // <5>
  }
}

分析下上面的例子,咱們在一個類中同時定義了Subject和Observer, 由於Subject和Observer是trait,而不是一個世紀的類型,因此咱們又定義了Subject和Object爲S和O的類型上界,這就意味着S和O分別是Subject和Object的子類型。server

在4的位置,notifyObservers須要通知存儲在Subject中的observers,調用Observer的receiveUpdate方法。對象

receiveUpdate須要接受一個具體的子類型S,可是4的位置receiveUpdate(this)中傳遞的參數是this即Subject,這樣會致使編譯失敗。繼承

那麼若是咱們想實如今Subject中傳遞S類型的實例怎麼辦?這時候就可使用到自定義類型標記了。教程

咱們看下面改造的例子:get

abstract class SubjectObserver {
  type S <: Subject
  type O <: Observer

  trait Subject {
    self: S =>         // <1>                                              
    private var observers = List[O]()

    def addObserver(observer: O) = observers ::= observer

    def notifyObservers() = observers.foreach(_.receiveUpdate(self)) // <2>
  }

  trait Observer {
    def receiveUpdate(subject: S): Unit
  }
}

變化的點在1和2,位置1定義了一個自定義類型標記,它說明了兩個意思:博客

  1. self指向了this
  2. self是S類型的實例

在2中,咱們直接傳入self就好了,這裏self也能夠換作其餘的字面量。

下面咱們看下怎麼使用這個類:

case class Button(label: String) {                                  
  def click(): Unit = {}   // <1>
}

object ButtonSubjectObserver extends SubjectObserver {               
  type S = ObservableButton  // <2>
  type O = Observer

  class ObservableButton(label: String) extends Button(label) with Subject {
    override def click() = {
      super.click()
      notifyObservers()
    }
  }
}

import ButtonSubjectObserver._

class ButtonClickObserver extends Observer {                 
 val clicks = new scala.collection.mutable.HashMap[String,Int]()    // <3>

  def receiveUpdate(button: ObservableButton): Unit = {
    val count = clicks.getOrElse(button.label, 0) + 1
    clicks.update(button.label, count)
  }
}

咱們須要定義一個Object繼承SubjectObserver, 並在它的內部再定義兩個class實現相應的trait。

看下咱們如何給S和O賦值:

type S = ObservableButton  // <2>
  type O = Observer

如今一個觀察者模式就完成了。這個例子中咱們使用自類型標記來解決使用抽象類型成員時帶來的問題。

下面咱們再舉一個更復雜一點的例子:

trait Persistence { def startPersistence(): Unit }                   // <1>
trait Midtier { def startMidtier(): Unit }
trait UI { def startUI(): Unit }

trait Database extends Persistence {                                 // <2>
  def startPersistence(): Unit = println("Starting Database")  
}
trait BizLogic extends Midtier {
  def startMidtier(): Unit = println("Starting BizLogic")  
}
trait WebUI extends UI {
  def startUI(): Unit = println("Starting WebUI")  
}

trait App { self: Persistence with Midtier with UI =>                // <3>
  
  def run() = {
    startPersistence()
    startMidtier()
    startUI()
  }
}

object MyApp extends App with Database with BizLogic with WebUI      // <4>
                                                                     
MyApp.run

咱們定義了一個三層的應用程序,而後在App中調用他們。

在App中咱們這樣定義自定義類型:

self: Persistence with Midtier with UI =>

意思是App的實例應該是Persistence,Midtier和UI的子類型。

因此在定義App對象的時候就必需要這樣定義:

object MyApp extends App with Database with BizLogic with WebUI

使用自類型標記實際上與使用繼承和混入等價(除了沒有定義self 之外):

trait App extends Persistence with Midtier with UI {
def run = { ... }
}

也有一些特殊狀況下,自類型標記的行爲不一樣於繼承。但在實踐中,這兩種方法能夠相互替換使用。

事實上,這兩種方法表達了不一樣的意圖。剛剛展現的基於繼承的實現代表應用程序是Persistence、Midtier 和UI 的一個子類型。與此相反,自類型標記則更加明確地表示其行爲的組合是經過混入實現的。

更多教程請參考 flydean的博客

相關文章
相關標籤/搜索