當(dāng)你開(kāi)始使用繼承來(lái)重用代碼時(shí),你入門(mén)了;當(dāng)你開(kāi)始避免使用繼承來(lái)重用代碼時(shí),你成熟了
這是我以前在知乎上看到關(guān)于類(lèi)繼承作用的回答,雖不完全正確,卻十分明確的表達(dá)出了好的代碼應(yīng)避免類(lèi)繼承而盡量使用類(lèi)組合。Scala 顯然也非常贊同這一點(diǎn),以至于有了 trait,又叫做特質(zhì)。當(dāng)我們定義特質(zhì)時(shí),應(yīng)該要遵循這樣的原則:一個(gè) trait 只干一件事,如果要干多件事,就定義多個(gè) trait,然后使用一個(gè)類(lèi)來(lái) extends 這些 traits
定義 trait
trait 的定義與 class 類(lèi)似:
scala> trait T {
| }
defined trait T
當(dāng)然,trait 可以包含成員和方法,并且:
- trait 中的成員可以僅聲明,也可以聲明并指定值
- trait 中的方法可以有實(shí)現(xiàn),也可以只有聲明而沒(méi)有實(shí)現(xiàn)
scala> trait T {
| val a: Int
| val b: Int = 1
|
| def getA(): Int
| def getB() = b
| }
defined trait T
對(duì)比而言,類(lèi)一旦包含未定義的方法就必須聲明為 abstract;而 Java 的接口中的方法是不能實(shí)現(xiàn)的,必須是抽象方法。如果 trait 既為實(shí)現(xiàn)它所聲明的方法,也沒(méi)有定義或聲明其他成員,那么在字節(jié)碼級(jí)別,該 trait 其實(shí)是接口是相同的
另一個(gè)與類(lèi)不同的是,trait 主構(gòu)造函數(shù)不允許有參數(shù)列表,并且不允許為 trait 定義輔助構(gòu)造函數(shù)
混入多個(gè) trait
Scala 類(lèi)只能有一個(gè)父類(lèi),但可以混入多個(gè) trait,當(dāng)要混入多個(gè) traits 或已經(jīng)繼承了某個(gè)父類(lèi)時(shí),需要使用關(guān)鍵字 with,如下例:
scala> trait T {
| val a: Int
| val b: Int = 1
|
| def getA(): Int
| def getB() = b
| }
defined trait T
scala>
scala> trait Q {
| def currentTime: String = System.currentTimeMillis().toString
| }
defined trait Q
scala>
scala> class X extends T with Q {
| override val a = 1
| override def getA(): Int = a
| }
defined class X
當(dāng)類(lèi)混入 trait 時(shí),需要實(shí)現(xiàn) trait 中為實(shí)現(xiàn)的成員和方法。要混入多個(gè) trait 是為了保證『高內(nèi)聚』,通俗說(shuō)就是一個(gè) trait 只干一件事,如果要干多件事,就定義多個(gè) trait 然后混入它們
當(dāng)你繼承的父類(lèi)和混入的特質(zhì)或混入的不同特質(zhì)之間有同名方法時(shí)可能會(huì)有沖突,分為以下幾種情況:
- trait 中的方法未實(shí)現(xiàn):不會(huì)沖突
scala> class C {
| def a: String = "a"
| }
defined class C
scala>
scala> trait T {
| def a: String
| }
defined trait T
scala>
scala> trait Q extends C with T {}
defined trait Q
- trait 中的方法實(shí)現(xiàn)了且與父類(lèi)中的方法參數(shù)列表及返回類(lèi)型相同:會(huì)沖突
scala> class C {
| def a: String = "a"
| }
defined class C
scala>
scala> trait T {
| def a: String = ""
| }
defined trait T
scala>
scala> trait Q extends C with T {}
<console>:9: error: trait Q inherits conflicting members:
method a in class C of type => String and
method a in trait T of type => String
(Note: this can be resolved by declaring an override in trait Q.)
trait Q extends C with T {}
^
- trait 中的方法實(shí)現(xiàn)了且與父類(lèi)中的參數(shù)列表相同,返回類(lèi)型不同:會(huì)沖突
scala> class C {
| def a: String = "a"
| }
defined class C
scala>
scala> trait T {
| def a: Int = 1
| }
defined trait T
scala>
scala> trait Q extends C with T {}
<console>:9: error: trait Q inherits conflicting members:
method a in class C of type => String and
method a in trait T of type => Int
(Note: this can be resolved by declaring an override in trait Q.)
trait Q extends C with T {}
^
- trait 中的方法實(shí)現(xiàn)了且與父類(lèi)的參數(shù)列表不同,返回類(lèi)型相同:不會(huì)沖突
scala> class C {
| def a: String = "a"
| }
defined class C
scala>
scala> trait T {
| def a( i: Int ): String = i.toString
| }
defined trait T
scala>
scala> trait Q extends C with T {}
defined trait Q
trait 的繼承
一個(gè) trait 同樣可以混入其他 trait 或繼承類(lèi):
scala> class C {
| def currentTime: String = System.currentTimeMillis().toString
| }
defined class C
scala>
scala> trait T {
| def random: Int
| }
defined trait T
scala>
scala> trait Q extends C with T {}
defined trait Q
雖然 Scala 語(yǔ)言支持你這么做,但我個(gè)人并不推薦
**傳送門(mén): **Scala 在簡(jiǎn)書(shū)目錄
歡迎關(guān)注我的微信公眾號(hào):FunnyBigData
