Scala入門2(特質與疊加在一起的特質)
一、介紹
參考http://luchunli.blog.51cto.com/2368057/1705025
我們知道,如果幾個類有某些共通的方法或者字段,那麽從它們多重繼承時,就會出現麻煩。所以Java被設計成 不支持多重繼承,但可以實現任意多的接口。接口只能包含抽象方法,不能包含字段。
特質 trait 是Scala中特有的一項特點,不同於C#與Java,如果一定要拿C#與Java中的某項特點作對比的話,最接近的應該是接口,但是C#與Java中的接口是不允許帶有方法實現的,而Scala中的Trait是可以帶有方法實現的。這樣做的好處需要某個trait就拿來用,而不需要重復實現接口。
所有的Java接口都可以作為Scala特質來使用。與Java一樣,Scala類只能有一個超類,可以有任意數量的特質。
特質的定義使用保留字trait,具體語法與類定義類似,除了不能擁有構造參數
1 trait reset { 2 def reset (m : Int, n : Int) = if (m >= n) 1; 3 }
一旦特質被定義了,就可以混入到類中
1 class week extends reset {......}
當要混入多個特質時,利用with保留字
1 class week extends reset with B with C {......}
特質的成員可以是抽象的,而且不需要使用abstract聲明。同樣,重寫特質的抽象方法無需給出override。但是,多個特質重寫同一個特質的抽象方法需要給出override。
除了在類定義中混入特質外,還可以在特質定義中混入特質
1 trait reseting extends reset {......}
在對象構造時混入特質
1 val file = new month with reseting
特質的構造是有順序的,從左到右被構造
1 /** 2 * @author lucl 3 */ 4 class Teacher { // 實驗用的空類 5 println("===============I‘m Teacher."); 6 } 7 8 trait TTeacher extends Teacher { 9 println("===============I‘m TTeacher.")10 def teach; // 虛方法,沒有實現 11 } 12 13 trait TPianoTeacher extends Teacher { 14 println("===============I‘m TPianoTeacher.") 15 def playPiano = { // 實方法,已實現 16 println("I‘m playing piano."); 17 } 18 } 19 20 class PianoPlayingTeacher extends Teacher with TTeacher with TPianoTeacher { 21 println("===============I‘m PianoPlayingTeacher.") 22 def teach = { // 定義虛方法的實現 23 println("I‘m teaching students."); 24 } 25 } 26 27 object TraitOps { 28 def main (args : Array[String]) { 29 var p = new PianoPlayingTeacher; 30 p.teach; 31 p.playPiano; 32 } 33 } 34 35 /** 36 ===============I‘m Teacher. 37 ===============I‘m TTeacher. 38 ===============I‘m TPianoTeacher. 39 ===============I‘m PianoPlayingTeacher. 40 I‘m teaching students. 41 I‘m playing piano. 42 */
二、例子
我們的例子中定義了一個抽象類Aminal表示所有的動物,然後定義了兩個trait Flyable和Swimable分別表示會飛和會遊泳兩種特征。
Aminmal的實現(定義了walk方法,實現了breathe方法)
1 abstract class Animal { 2 def walk(speed : Int); 3 4 def breathe () = { 5 println("animal breathes."); 6 } 7 }
Flyable和Swimable兩個 trait的實現
1 /** 2 * @author lucl 3 * 有兩個方法,一個抽象方法一個已實現方法 4 */ 5 trait Flyable { 6 def hasFather = true; 7 def fly; 8 } 9 10 package com.mtrait 11 12 /** 13 * @author lucl 14 * 只有一個抽象方法 15 */ 16 trait Swimable { 17 def swim; 18 }
我們定義一種動物,它既會飛也會遊泳,這種動物是魚鷹 FishEagle
1 /** 2 * @author lucl 3 */ 4 class FishEagle extends Animal with Flyable with Swimable { 5 /** 6 * 實現抽象類的walk方法 7 */ 8 override def walk(speed: Int) = { 9 println ("Fish eagle walk with speed : " + speed + "."); 10 } 11 12 /** 13 * 實現trait Flyable的方法 14 */ 15 override def fly = { 16 println("Fish eagle fly fast."); 17 } 18 19 /** 20 * 實現trait Swimable的方法 21 */ 22 override def swim { 23 println("Fish eagle swim fast."); 24 } 25 } 26 27 object FishEagle { 28 def main (args : Array[String]) { 29 val fish = new FishEagle; 30 fish.walk(100); 31 fish.fly; 32 fish.swim; 33 println("fish eagle has father ? " + fish.hasFather + "."); 34 // println(fish.swim); // 輸出為() 35 36 println(); 37 val flyable : Flyable = fish; 38 flyable.fly; 39 40 val swimable : Swimable = fish; 41 swimable.swim; 42 } 43 } 44 45 /** 46 輸出結果: 47 Fish eagle walk with speed : 100. 48 Fish eagle fly fast. 49 Fish eagle swim fast. 50 fish eagle has father ? true. 51 52 Fish eagle fly fast. 53 Fish eagle swim fast. 54 */
trait很強大,抽象類能做的事情,trait都可以做,它的長處在於可以多繼承。
trait和抽象類的區別在於抽象類是對一個繼承鏈的,類和類之前確實有父子類的繼承關系,而trait則如其名字,表示一種特征,可以多繼承。
在對象中混入trait
/** * 單獨的日誌模塊 * 只是標識要記錄日誌,但沒有明確定義如何記錄日誌 */ trait Logger { def log (msg : String) {} } /** * 記錄日誌的具體實現類 */ trait WriteLogger extends Logger { override def log (msg : String) = {println("WriteLogger : " + msg);} } /** * 需要執行的業務操作 */ trait Action { def doAction(action : String); } class TraitActionImpl extends Action { override def doAction(op : String) = println(op); } class LoggerActionImpl extends Action with Logger { override def doAction(op : String) = { println(op); // 如果確實需要日誌功能但暫不清楚以何種形式記錄日誌時,可以采用該方法; // 當明確了記錄日誌的方式後,再通過如下在對象中混入trait實現。 log (op); } } /** * @author lucl */ object TraitOps { def main (args : Array[String]) { // println("===================aaaaaa========================"); // 類本身與記錄日誌Logger沒有關系,但是在對象中混入trait的代碼後,就具備了日誌的功能 val actionA = new TraitActionImpl with WriteLogger; val op = "業務操作"; actionA.doAction(op); actionA.log(op); // println("===================bbbbbb========================"); // 類實現了Logger,但日誌記錄是空的操作 val loggerA = new LoggerActionImpl; loggerA.doAction(op); println("===================cccccc========================"); // 類實現了Logger,通過在類定義中混入trait實現了自己的記日誌的功能 val loggerB = new LoggerActionImpl with WriteLogger; loggerB.doAction(op); } } /** 輸出結果: ===================aaaaaa======================== 業務操作 WriteLogger : 業務操作 ===================bbbbbb======================== 業務操作 ===================cccccc======================== 業務操作 WriteLogger : 業務操作 */
三、總結
簡單來說,Scala的trait就是類似於Java的接口。使一個類能實現多種功能。
Scala入門2(特質與疊加在一起的特質)