Scala入門3(特質線性化)
嘗試設計一套特質,靈活的改動整數隊列。隊列有兩種操作: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 extendsQueue2 { 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(特質線性化)