Scala的自定义类型标记
Scala的自定義類型標記
Scala中有很多千奇百怪的符號標記,看起來是那么的獨特,就像是一杯dry martini…好像黑夜中的螢火蟲,那么耀眼,那么出眾。
好了言歸正傳,這一篇文章我們會講一下Scala中的自定義類型標記,通過自定義類型標記可以將this指向額外的類型期望。
我們先看一個觀察者模式的例子:
abstract class SubjectObserver { type S <: Subject // <1> type O <: Observer // <2>trait Subject { private var observers = List[O]() // <3>def addObserver(observer: O) = observers ::= observerdef 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的子類型。
在4的位置,notifyObservers需要通知存儲在Subject中的observers,調(diào)用Observer的receiveUpdate方法。
receiveUpdate需要接受一個具體的子類型S,但是4的位置receiveUpdate(this)中傳遞的參數(shù)是this即Subject,這樣會導(dǎo)致編譯失敗。
那么如果我們想實現(xiàn)在Subject中傳遞S類型的實例怎么辦?這時候就可以使用到自定義類型標記了。
我們看下面改造的例子:
abstract class SubjectObserver {type S <: Subjecttype O <: Observertrait Subject {self: S => // <1> private var observers = List[O]()def addObserver(observer: O) = observers ::= observerdef notifyObservers() = observers.foreach(_.receiveUpdate(self)) // <2>}trait Observer {def receiveUpdate(subject: S): Unit} }變化的點在1和2,位置1定義了一個自定義類型標記,它說明了兩個意思:
在2中,我們直接傳入self就行了,這里self也可以換做其他的字面量。
下面我們看下怎么使用這個類:
case class Button(label: String) { def click(): Unit = {} // <1> }object ButtonSubjectObserver extends SubjectObserver { type S = ObservableButton // <2>type O = Observerclass 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) + 1clicks.update(button.label, count)} }我們需要定義一個Object繼承SubjectObserver, 并在它的內(nèi)部再定義兩個class實現(xiàn)相應(yīng)的trait。
看下我們?nèi)绾谓oS和O賦值:
type S = ObservableButton // <2>type O = Observer現(xiàn)在一個觀察者模式就完成了。這個例子中我們使用自類型標記來解決使用抽象類型成員時帶來的問題。
下面我們再舉一個更復(fù)雜一點的例子:
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我們定義了一個三層的應(yīng)用程序,然后在App中調(diào)用他們。
在App中我們這樣定義自定義類型:
self: Persistence with Midtier with UI =>意思是App的實例應(yīng)該是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 = { ... } }也有一些特殊情況下,自類型標記的行為不同于繼承。但在實踐中,這兩種方法可以相互替換使用。
事實上,這兩種方法表達了不同的意圖。剛剛展示的基于繼承的實現(xiàn)表明應(yīng)用程序是Persistence、Midtier 和UI 的一個子類型。與此相反,自類型標記則更加明確地表示其行為的組合是通過混入實現(xiàn)的。
更多精彩內(nèi)容且看:
- 區(qū)塊鏈從入門到放棄系列教程-涵蓋密碼學(xué),超級賬本,以太坊,Libra,比特幣等持續(xù)更新
- Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續(xù)更新
- Spring 5.X系列教程:滿足你對Spring5的一切想象-持續(xù)更新
- java程序員從小工到專家成神之路(2020版)-持續(xù)更新中,附詳細文章教程
更多教程請參考 flydean的博客
總結(jié)
以上是生活随笔為你收集整理的Scala的自定义类型标记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新版gitbook导出pdf
- 下一篇: 单向散列函数