(轉自http://www.qqread.com/other-devtool/f484284.html)
與Java相似之處
Scala類型系統的基礎部分是與Java非常相像的。Scala與Java一樣有單一的根類,Java通過接口來實現多重繼承,而Scala則通過特徵(trait)來實現(Scala的特徵可以包含實現代碼,這當然是與Java接口不同的。不過由於特徵自己具有類型的功能,所以對於沒有包含實現代碼的特徵,可以認爲與Java的接口是等價的)
用Trait來實現混入(mix-in)式的多重繼承
Scala裏相當於Java接口的是Trait(特徵)。Trait的英文意思是特質和性狀(本文稱其爲特徵),實際上他比接口還功能強大。與接口不同的是,它還可以定義屬性和方法的實現。Scala中特徵被用於服務於單一目的功能模塊的模塊化中。通過混合這種特徵(模塊)羣來實現各種應用程序的功能要求,Scala也是按照這個構想來設計的。
一般情況下Scala的類只能夠繼承單一父類,但是如果是特徵的話就可以繼承多個,從結果來看就是實現了多重繼承。就看一下下面的例子吧。爲了辨認方便,此後的特徵名稱前都加上前綴字母T。特徵既可以繼承類也可以繼承其他特徵。
- class Person ; //實驗用的空類
- trait TTeacher extends Person {
- def teach //虛方法,沒有實現
- }
- trait TPianoPlayer extends Person {
- def playPiano = {println("I’m playing piano. ")} //實方法,已實現
- }
- class PianoplayingTeacher extends Person with TTeacher with TPianoPlayer {
- def teach = {println("I’m teaching students. ")} //定義虛方法的實現
- }
那麼就實際運行一下吧。
- scala> val t1 = new PianoplayingTeacher
- t1: PianoplayingTeacher = PianoplayingTeacher@170a650
- scala> t1.playPiano
- I’m playing piano.
- scala> t1.teach
- I’m teaching students.
- scala> val tanakaTaro = new Person with TTeacher with TPianoPlayer {
- | def teach = {println("I'm teaching students.")} }
- tanakaTaro: Person with TTeacher with TPianoPlayer = $anon$1@5bcd91
- scala> tanakaTaro playPiano
- I’m playing piano.
- scala> tanakaTaro teach
- I'm teaching students.
充分利用特徵的功能之後,就能方便地實現現今流行的面向方面編程(AOP)了。
首先,用特徵來聲明表示基本動作方法的模塊Taction。
- trait TAction {
- def doAction
- }
- trait TBeforeAfter extends TAction {
- abstract override def doAction {
- println("/entry before-action") //doAction的前置處理
- super.doAction // 調用原來的處理
- println("/exit after-action") //doAction的後置處理
- }
- }
那麼將實際執行的實體類RealAction作爲TAction的子類來實現吧。
- class RealAction extends TAction {
- def doAction = { println("** real action done!! **") }
- }
- scala> val act1 = new RealAction with TBeforeAfter
- act1: RealAction with TBeforeAfter = $anon$1@3bce70
- scala> act1.doAction
- /entry before-action
- ** real action done!! **
- /exit after-action
- trait TTwiceAction extends TAction {
- abstract override def doAction {
- for ( i <- 1 to 2 ) { // 循環執行源方法的方面
- super.doAction // 調用源方法doAction
- println( " ==> No." + i )
- }
- }
- }
- scala> val act2 = new RealAction with TBeforeAfter with TTwiceAction
- act2: RealAction with TBeforeAfter with TTwiceAction = $anon$1@1fcbac1
- scala> act2.doAction
- /entry before-action
- ** real action done!! **
- /exit after-action
- ==> No.1
- /entry before-action
- ** real action done!! **
- /exit after-action
- ==> No.2
- scala> val act3 = new RealAction with TTwiceAction with TBeforeAfter
- act3: RealAction with TTwiceAction with TBeforeAfter = $anon$1@6af790
- scala> act3.doAction
- /entry before-action
- ** real action done!! **
- ==> No.1
- ** real action done!! **
- ==> No.2
- /exit after-action
這樣執行後,原來的實現方法被循環執行了兩次,但是before/after則在循環以外整體只執行了一次。這是根據with語句定義的順序來執行的,知道了這原理之後也就沒有什麼奇怪的了。Scala特性的如此混入順序是和AspectJ的方面以及Spring的interceptor相同的。
這樣不僅是before和after動作,只要更改了特徵的實現就可以將各種方面動態地加入到原來的對象中去了,讀者自己也可以嘗試一下各種其他情況。
在Java中通過Decorator或Template Method模式來想盡辦法實現的功能,在Scala中只要通過特徵就可以輕鬆到手了。從這還可以延展開來,通過在原來的方法中插入掛鉤的方法,即所謂的攔截者式面向方面的方法,就可以輕鬆地將各個方面通過特徵來組件化了。
請讀者如果想起Scala是怎樣的強類型和靜態化語言的話,那麼就能夠明白通過特徵來加入新功能的特點給他帶來了多大的靈活性。