特質,很像j**a中的介面,但是又有些不同,比如實現方法,當然j**a8也可以在介面中實現乙個方法了,但是只能定義乙個default方法。
當做介面使用
//特質
trait
logger
trait
consolelogger
extends
logger //不需要寫 overrride
//在重寫 特質的抽象方法是不需要寫 overrride關鍵字
//如果需要的特質不止乙個,可以使用with連線
//特質中的方法並不一定是抽象的
def write(msg:string)
}
含有具體實現
例子**於 快學scala
先定義這麼乙個trait
trait
logged
}
寫乙個class繼承它
class
s**ingaccount
extends
account
with
logged
}
從**上看,在logged 中,我們並沒有實現此方法,所以這裡的log是無意義的。
但是在scala中並非如此。
trait
consolelogged
extends
logged
}
定義一trait繼承logged,並實現log方法,然後鑼鼓一響,好戲開場
object
testtrait
extends
val acct = new s**ingaccount with consolelogged
acct.withdraw(2.2)
}
你會控制台有日誌列印。
下面看下多個trait的執行順序,是一件非常有意思的事情,首先寫幾個trait,**如下:
trait
consolelogged
extends
logged
}
trait
timestamplogger
extends
logged
}
trait
shortlogger
extends
logged
}
測試:
val acct = new s**ingaccount with consolelogged
acct.withdraw(3.2)
val acct1 = new s**ingaccount with consolelogged with timestamplogger with shortlogger
acct1.withdraw(2.2)
val acct2=new s**ingaccount with timestamplogger with consolelogged with shortlogger
acct2.withdraw(2.5)
val acct3=new s**ingaccount with consolelogged with shortlogger with timestamplogger
acct3.withdraw(2.5)
val acct4=new s**ingaccount with timestamplogger with shortlogger with consolelogged
acct4.withdraw(2.5)
輸出如下:
console=>s**ingaccount print =>
s**ingaccount p...short.print at.thu aug 13
17:54:17 cst 2015
console=>s**ingaccount p...short
s**ingaccount print =>.print at.thu aug 13
17:54:17 cst 2015
console=>s**ingaccount print =>
從列印結果分析看,如果trait是從左到右開始執行,那麼第三條和第五條沒有輸出是無法解釋的;那麼如果從右開始執行呢,可以看到第二條輸出,先執行shortlogger,呼叫super.log,執行timestamplogger,那麼consolelogged似乎沒有被呼叫;以此解釋第三條輸出也是說的通的,而timestamplogger沒有被執行,那麼第四條資料呢,只是執行了timestamplogger,同理第五條也是如此,如果沒有super,似乎前面的trait中的方法不會被呼叫,但是,如果我在timestamplogger如此做的話,需要打上abstract標籤,輸出結果和上面相同,這裡有些疑問,暫且存疑。
在特質中宣告字段
在trait中欄位宣告可以是具體的,也可以是抽象的,如果在字段宣告時初始化,就是具體的。如果有子類繼承吃trait,改字段在jvm中,實際上是屬於子類的字段,和子類字段放在一起。
宣告抽象字段
trait
shortlogger
extends
logged
}
需要在子類中對其初始化,例如:
class
s**ingaccount
extends
account
with
shortlogger
override
val maxlength: int = 15
}
也可以如此:
val acct1 = new s**ingaccount with consolelogged with timestamplogger with shortlogger
特質的構造順序
trait是有構造順序的,初始化乙個類,構造順序如下:
先呼叫超類的構造方法
trait構造方法在超類構造方法之後和類的構造方法之前執行
trait由左到右被構造(待驗證)
在trait中,父trait首先被構造
如果多個trait共有乙個父trait,而父trait已經被構造,則不會父trait不會再被構造
所有的trait被構造完畢,類被構造
new s**ingaccount with consolelogged with timestamplogger with shortlogger
根據《快學scala》中介紹的構造順數,線性化相反的方向,串接並去掉重複選項,右側勝出
以上面的為例子:
s**ingaccount >>shortlogger>>timestamplogger >>consolelogged >>logged>>account
但是輸出結果:
s**ingaccount p...short
.print at.fri aug 14
17:17:41 cst 2015
但是從輸出結果上分析,少了consolelogged 的輸出結果,至於為什麼,恕在下才疏學淺,還沒有找到結果。
scala執行時需要jvm的,但是jvm只支援單一繼承。那麼scala的trait在jvm中是怎麼被翻譯的呢?
如果scala只有抽象方法時,trait被翻譯成介面,如果trait有具體的方法實現,scala會建立乙個伴生類,該伴生類用靜態方法存放特質的方法,這些伴生類中不會有任何字段。特質中的字段會對應到介面中抽象的setter和getter方法。如果trait繼承自某個超類,伴生類不會繼承改超類,該超類會被任何實現該特質的類繼承。
Scala 系列 特質 Trait
本文主要對scala中特質的概念與使用進行介紹 特質是scala裡面 復用的基礎單元。與 python 不同,python 子類可以繼承自多個父類,而 scala 不允許乙個類從從個超類繼承,只能繼承唯一的超類。但是 scala 允許乙個類混入任意數量的特質,混入就是指類使用了特質提供的方法。那麼特...
Scala基礎 7 特質(Trait)
特質定義使用關鍵字trait trait carid上面定義了乙個trait,裡面包含乙個抽象欄位id和抽象方法currentid。注意,抽象方法不需要使用abstract關鍵字,特質中沒有方法體的方法,預設就是抽象方法。trait定義好之後,就可以使用extends或with關鍵字將trait混入...
scala學習(十五) trait
下面是乙個trait的簡單例子,裡面包含的trait的基本用法,trait的繼承 trait logger def warn msg string trait logger2 trait logger3 import scala.reflect.class dog extends logger wi...