[7] - trait

當(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

FunnyBigData
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這篇講義只講scala的簡(jiǎn)單使用,目的是使各位新來(lái)的同事能夠首先看懂程序,因?yàn)?scala 有的語(yǔ)法對(duì)于之前使用習(xí)...
    MrRobot閱讀 3,037評(píng)論 0 10
  • 將trait作為接口使用Scala中的Triat是一種特殊的概念,可以將Trait作為接口來(lái)使用,此時(shí)的Triat...
    sunnyzhu92閱讀 931評(píng)論 0 0
  • 一直想做一個(gè)繪畫(huà)基礎(chǔ)的教程。沒(méi)有太多時(shí)間做不了視頻的教程,然后就做一個(gè)我覺(jué)得能表達(dá)清楚的文字的教程吧。 對(duì)于零基礎(chǔ)...
    懿暖醬閱讀 2,175評(píng)論 7 5
  • 七點(diǎn)鐘,我餓著肚子開(kāi)始上課 廚房里的他在井井有條的做飯 我感動(dòng)之余 不免心酸 我要找這樣一個(gè)善待我的人 看得清我的...
    躲起來(lái)的攖寧閱讀 132評(píng)論 0 0
  • 許巍的新專輯出了 是個(gè)叫高曉松的矮大緊作的詞 兩者的結(jié)合 產(chǎn)生了奇妙的化學(xué)反應(yīng) 最近刷遍朋友圈的心靈雞湯文 全都在...
    Luns醬閱讀 1,750評(píng)論 0 0

友情鏈接更多精彩內(nèi)容