1. 程式人生 > >Scala入門3(特質線性化)

Scala入門3(特質線性化)

相同 object 完成 可能 args scala 結構 放置 mutable

  嘗試設計一套特質,靈活的改動整數隊列。隊列有兩種操作:put把整數放入隊列,get從尾部取出它們。隊列是先進先出的,get應該依照入隊列的順序取數據。提示:可以用mutable.ArrayBuffer 模擬隊列在報告中體現出類的線性化特性,要求擴展類實現如下三個功能1.Doubling 把放到隊列中的數字翻倍;2.Incrementing 把放到隊列的數據增加1;3.過濾掉隊列中的負數

 1 abstract class Queue2 {
 2   println("查看調用順序Queue")
 3   def get:Int
 4   def put(num:Int)
 5 }
 6 trait Doubling extends
Queue2 { 7 println("查看調用順序Doubling") 8 abstract override def put(x: Int) { super.put(2*x) } 9 } 10 trait Incrementing extends Queue2 { 11 println("查看調用順序Incrementing") 12 abstract override def put(x: Int) { super.put(x+1) } 13 } 14 trait Filtering extends Queue2 { 15 println("查看調用順序Filtering")
16 abstract override def put(x: Int){ 17 if(x >= 0) super.put(x) 18 } 19 } 20 class NewQueue extends Queue2{ 21 println("查看調用順序NewQueue") 22 private val numArrayBuffer = new ArrayBuffer[Int] 23 def get() = numArrayBuffer.remove(0) 24 def put(x: Int) = { 25 numArrayBuffer += x 26 }
27 } 28 object test5{ 29 def main(args: Array[String]): Unit = { 30 val queue = new NewQueue with Doubling 31 queue.put(1) 32 println(queue.get()) 33 34 val queue2 = new NewQueue with Doubling with Incrementing 35 queue2.put(10) 36 println(queue2.get()) 37 38 } 39 }

技術分享

  首先我們知道特質構造器的調用順序是:

  1.調用超類的構造器;

  2.特質構造器在超類構造器之後、類構造器之前執行;

  3.特質由左到右被構造;

  4.每個特質當中,父特質先被構造;

  5.如果多個特質共有一個父特質,父特質不會被重復構造

  6.所有特質被構造完畢,子類被構造。

  混入的順序很重要,越靠近右側的特質越先起作用。當你調用帶混入的類的方法時,最右側特質的方法首先被調用。如果那個方法調用了super,它調用其左側特質的方法,以此類推。

這裏很神奇的一點是,輸入10,輸出居然是22而不是21。貌似是Incrementing的put首先被調用,然後Doubing的put第二個被調用。但為什麽在顯示語句中,我們發現先顯示的是“查看調用順序Doubling”呢?

  我們來看看類的線性化的含義:

  特質是一種繼承多個類似於類的結構的方式,但是它與多重繼承有很重要的區別。其中一個尤為重要:super的解釋。

  對於多重繼承來說,super調用導致的方法調用可以在調用發生的地方明確決定;對於特質來說,方法調用是由類和混入到類的特質的線性化(linearization)所決定的。這種差別使得上面的特質的堆疊成為可能。

  在多重繼承的語言中,調用相同的方法,編譯規則會決定哪個超類最終勝出,而調用該超類的指定方法。

  而在Scala中,當你使用new實例化一個類的時候,Scala把這個類和所有它繼承的類還有他的特質以線性的次序放在一起。然後,當你在其中的一個類中調用super,被調用的方法就是方法鏈的下一節。除了最後一個調用super之外的方法,其凈結果就是可堆疊的行為。

  所以,在這裏我們看到調用順序的確是先Doubling後Incrementing,但是在線性的過程中,先執行的是最後一層,即越靠近右側的特質越先起作用。先+1,再*2,最後put。

  舉例:

 1 class A{
 2   println("查看調用順序A")
 3   def m(s:String) = println(s"A($s)")
 4 }
 5 trait B extends A{
 6   println("查看調用順序B")
 7   override def m(s:String) = super.m(s"B($s)")
 8 }
 9 trait C extends A{
10   println("查看調用順序C")
11   override def m(s:String) = super.m(s"C($s)")
12 }
13 trait D extends A{
14   println("查看調用順序D")
15   override def m(s:String) = super.m(s"D($s)")
16 }
17 trait E extends C{
18   println("查看調用順序E")
19   override def m(s:String) = super.m(s"E($s)")
20 }
21 trait F extends C{
22   println("查看調用順序F")
23   override def m(s:String) = super.m(s"F($s)")
24 }
25 class G extends D with E with F with B{
26   println("查看調用順序G")
27   override def m(s:String) = super.m(s"G($s)")
28 }
29 object t{
30   def main(args: Array[String]): Unit = {
31     val x = new G
32     x.m("")
33   }
34 }

  這段代碼最後的輸出結果是:

技術分享

  為什麽呢?

  G extends D with E with F with B

  D extends A

  E extends C,C extends A

  F extends C,C extends A

  B extends A

  1.從左往右,選擇離G的trait最近的進行放置在左邊,他的父類放在右邊

  2.依次將剩下的trait的也從左邊開始放置,如果其父類已經出現在右邊,則跳過

  3.在最右加入AnyRef和Any,完成構建

  1.GDA

  2.GECDA

  3.GFECDA

  4.GBFECDA

Scala入門3(特質線性化)