Scala 有趣的Trait

(轉自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。特徵既可以繼承類也可以繼承其他特徵。

  1. class Person ; //實驗用的空類
  2. trait TTeacher extends Person {  
  3. def teach //虛方法,沒有實現  
  4. }  
  5. trait TPianoPlayer extends Person {  
  6. def playPiano = {println("I’m playing piano. ")} //實方法,已實現  
  7. }  
  8. class PianoplayingTeacher extends Person with TTeacher with TPianoPlayer {  
  9. def teach = {println("I’m teaching students. ")} //定義虛方法的實現  
  10. }  
如上所示,可以連着多個with語句來混合多個特徵到一個類中。第一個被繼承源用extends,第二個以後的就用with語句。正如大家所知道的,可以生成實例的是非抽象(abstract)的類。另外請注意一下從特徵是不可以直接創建實例的。

那麼就實際運行一下吧。

 

  1. scala> val t1 = new PianoplayingTeacher  
  2. t1: PianoplayingTeacher = PianoplayingTeacher@170a650 
  3. scala> t1.playPiano  
  4. I’m playing piano.  
  5. scala> t1.teach  
  6. I’m teaching students.  
實際上如下所示,可以在創建對象時纔將特徵各自的特點賦予對象。

 

 

  1. scala> val tanakaTaro = new Person with TTeacher with TPianoPlayer {  
  2. | def teach = {println("I'm teaching students.")} }  
  3. tanakaTaro: Person with TTeacher with TPianoPlayer = $anon$1@5bcd91 
  4. scala> tanakaTaro playPiano  
  5. I’m playing piano.  
  6. scala> tanakaTaro teach  
  7. I'm teaching students.  
用特徵來方便地實現面向方面的編程

 

充分利用特徵的功能之後,就能方便地實現現今流行的面向方面編程(AOP)了。

首先,用特徵來聲明表示基本動作方法的模塊Taction。

 

  1. trait TAction {  
  2. def doAction  
  3. }  
接着作爲被加入的方面,定義一下加入了前置處理和後置處理的特徵TBeforeAfter。

 

 

  1. trait TBeforeAfter extends TAction {  
  2. abstract override def doAction {  
  3. println("/entry before-action") //doAction的前置處理  
  4. super.doAction // 調用原來的處理  
  5. println("/exit after-action") //doAction的後置處理  
  6. }  
  7. }  
通過上面的abstract override def doAction {}語句來覆蓋虛方法。具體來說這當中的super.doAction是關鍵,他調用了TAction的doAction方法。其原理是,由於doAction是虛方法,所以實際被執行的是被調用的實體類中所定義的方法。

 

那麼將實際執行的實體類RealAction作爲TAction的子類來實現吧。

 

  1. class RealAction extends TAction {  
  2. def doAction = { println("** real action done!! **") }  
  3. }  
接着就執行一下。

 

 

  1. scala> val act1 = new RealAction with TBeforeAfter  
  2. act1: RealAction with TBeforeAfter = $anon$1@3bce70 
  3. scala> act1.doAction  
  4. /entry before-action  
  5. ** real action done!! **  
  6. /exit after-action  
僅僅這樣還不好玩,接着爲他定義一下別的方面,然後將這些方面加入到同一對象的方法中。接着定義一個將源方法執行兩遍的方面。

 

 

  1. trait TTwiceAction extends TAction {  
  2. abstract override def doAction {  
  3. for ( i <- 1 to 2 ) { // 循環執行源方法的方面  
  4. super.doAction // 調用源方法doAction  
  5. println( " ==> No." + i )  
  6. }  
  7. }  
  8. }  
下面,將TBeforeAfter和TtwiceAction混合在一起後執行一下。

 

 

  1. scala> val act2 = new RealAction with TBeforeAfter with TTwiceAction  
  2. act2: RealAction with TBeforeAfter with TTwiceAction = $anon$1@1fcbac1 
  3. scala> act2.doAction  
  4. /entry before-action  
  5. ** real action done!! **  
  6. /exit after-action  
  7. ==> No.1 
  8. /entry before-action  
  9. ** real action done!! **  
  10. /exit after-action  
  11. ==> No.2 
伴隨着原來方法的before/after動作一起各自執行了兩次。接着將混入順序顛倒後再試一下。

 

 

  1. scala> val act3 = new RealAction with TTwiceAction with TBeforeAfter  
  2. act3: RealAction with TTwiceAction with TBeforeAfter = $anon$1@6af790 
  3. scala> act3.doAction  
  4. /entry before-action  
  5. ** real action done!! **  
  6. ==> No.1 
  7. ** real action done!! **  
  8. ==> No.2 
  9. /exit after-action  

 

這樣執行後,原來的實現方法被循環執行了兩次,但是before/after則在循環以外整體只執行了一次。這是根據with語句定義的順序來執行的,知道了這原理之後也就沒有什麼奇怪的了。Scala特性的如此混入順序是和AspectJ的方面以及Spring的interceptor相同的。

這樣不僅是before和after動作,只要更改了特徵的實現就可以將各種方面動態地加入到原來的對象中去了,讀者自己也可以嘗試一下各種其他情況。

在Java中通過Decorator或Template Method模式來想盡辦法實現的功能,在Scala中只要通過特徵就可以輕鬆到手了。從這還可以延展開來,通過在原來的方法中插入掛鉤的方法,即所謂的攔截者式面向方面的方法,就可以輕鬆地將各個方面通過特徵來組件化了。

請讀者如果想起Scala是怎樣的強類型和靜態化語言的話,那麼就能夠明白通過特徵來加入新功能的特點給他帶來了多大的靈活性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章