【Scala】特質(zhì)與特質(zhì)的線性化

特質(zhì)

Scala里相當(dāng)于Java接口的是Trait(特征)。實(shí)際上它比接口還功能強(qiáng)大。與接口不同的是,它還可以定義屬性和方法的實(shí)現(xiàn)。Scala中特征被用于服務(wù)于單一目的功能模塊的模塊化中。通過混合這種特征(模塊)群來實(shí)現(xiàn)各種應(yīng)用程序的功能要求,Scala也是按照這個(gè)構(gòu)想來設(shè)計(jì)的。

特質(zhì)的構(gòu)造順序

特質(zhì)也可以有構(gòu)造器,由字段的初始化和其他特質(zhì)體中的語(yǔ)句構(gòu)成。這些語(yǔ)句在任何混入該特質(zhì)的對(duì)象在構(gòu)造時(shí)都會(huì)被執(zhí)行。
構(gòu)造器的執(zhí)行順序:

  • 調(diào)用超類的構(gòu)造器;
  • 特質(zhì)構(gòu)造器在超類構(gòu)造器之后、類構(gòu)造器之前執(zhí)行;
  • 特質(zhì)由左到右被構(gòu)造;
  • 每個(gè)特質(zhì)當(dāng)中,父特質(zhì)先被構(gòu)造;
  • 如果多個(gè)特質(zhì)共有一個(gè)父特質(zhì),父特質(zhì)不會(huì)被重復(fù)構(gòu)造
  • 所有特質(zhì)被構(gòu)造完畢,子類被構(gòu)造。

構(gòu)造器的順序是類的線性化的反向。線性化是描述某個(gè)類型的所有超類型的一種技術(shù)規(guī)格。

用作可堆疊改變的特質(zhì)

特質(zhì)的一個(gè)主要用法是把瘦接口(有較少的方法)轉(zhuǎn)變成胖接口(有較多的方法)。
其第二個(gè)主要用法是為類提供可堆疊的改變。

考慮一個(gè)整數(shù)隊(duì)列的例子。隊(duì)列有兩種操作:put,把整數(shù)放入隊(duì)列;get,從尾部取出整數(shù)。

import scala.collection.mutable.ArrayBuffer

abstract class IntQueue {
    def get(): Int
    def put(x: Int)
}

class BasicIntQueue extends IntQueue {
    private val buf = new ArrayBuffer[Int]
    def get() = buf.remove(0)
    def put(x: Int) = { buf += x }
}

假設(shè)定義特質(zhì)執(zhí)行如下改動(dòng):

  • Doubling: 把所有放入隊(duì)列的數(shù)字加倍
  • Incrementing: 把所有放入隊(duì)列的數(shù)字增加
  • Filtering:從隊(duì)列中過濾掉負(fù)整數(shù)

這三種特質(zhì)代表了改動(dòng),因?yàn)樗鼈兏淖兞嗽缄?duì)列類的行為而并非定義了全新的隊(duì)列類。這三種特質(zhì)是可堆疊的,你可以選擇它們混入類中,獲得所需改動(dòng)的全新的類。

//在特質(zhì)中重寫抽象方法

trait Doubling extends IntQueue {
    abstract override def put(x: Int) { super.put(2*x) }
}

trait Incrementing extends IntQueue {
    abstract override def put(x: Int) { super.put(x+1) }
}

trait Filtering extends IntQueue {
    abstract override def put(x: Int){
        if(x >= 0) super.put(x)
    }
}

解釋:上面的代碼定義了超類IntQueue,這個(gè)定義意味著該特質(zhì)只能混入擴(kuò)展IntQueue的類中。
在特質(zhì)中聲明抽象方法中有super調(diào)用,在特質(zhì)里super調(diào)用時(shí)動(dòng)態(tài)綁定的;而在類中,由于繼承的抽象類,super調(diào)用時(shí)非法的。這里必須使用abstract override標(biāo)識(shí)符,它意味著特質(zhì)必須被混入某個(gè)具有期待方法的具體定義的類中,這種定義僅在特質(zhì)定義中使用。

演示:

scala> val queue = new BasicIntQueue with Doubling
queue: BasicIntQueue with Doubling = $anon$1@1d3eff4

scala> queue.put(10)

scala> queue.get()
res10: Int = 20

val queue = new BasicIntQueue with Doubling with Incrementing
queue: BasicIntQueue with Doubling with Incrementing = $anon$1@19de304

scala> queue.put(-1)

scala> queue.put(2)

scala> queue.put(3)

scala> queue.get
res3: Int = 0

scala> queue.get
res4: Int = 6

scala> queue.get
res5: Int = 8

混入的順序很重要,越靠近右側(cè)的特質(zhì)越先起作用。當(dāng)你調(diào)用帶混入的類的方法時(shí),最右側(cè)特質(zhì)的方法首先被調(diào)用。如果那個(gè)方法調(diào)用了super,它調(diào)用其左側(cè)特質(zhì)的方法,以此類推。上面的例子里,Incrementing的put首先被調(diào)用,然后Doubing的put第二個(gè)被調(diào)用。

特質(zhì)的線性化與多重集成

特質(zhì)是一種繼承多個(gè)類似于類的結(jié)構(gòu)的方式,但是它與多重繼承有很重要的區(qū)別。其中一個(gè)尤為重要:super的解釋。
對(duì)于多重繼承來說,super調(diào)用導(dǎo)致的方法調(diào)用可以在調(diào)用發(fā)生的地方明確決定;對(duì)于特質(zhì)來說,方法調(diào)用是由類和混入到類的特質(zhì)的線性化(linearization)所決定的。這種差別使得上面的特質(zhì)的堆疊成為可能。

在多重繼承的語(yǔ)言中,調(diào)用相同的方法,編譯規(guī)則會(huì)決定哪個(gè)超類最終勝出,而調(diào)用該超類的指定方法。
而在Scala中,當(dāng)你使用new實(shí)例化一個(gè)類的時(shí)候,Scala把這個(gè)類和所有它繼承的類還有他的特質(zhì)以線性的次序放在一起。然后,當(dāng)你在其中的一個(gè)類中調(diào)用super,被調(diào)用的方法就是方法鏈的下一節(jié)。除了最后一個(gè)調(diào)用super之外的方法,其凈結(jié)果就是可堆疊的行為。

線性化細(xì)節(jié)

Scala 的線性化的主要屬性可以用下面的例子演示:假設(shè)你有一個(gè)類 Cat,繼承自超類 Animal 以及兩個(gè)特質(zhì) Furry 和 FourLegged。 FourLegged 又?jǐn)U展了另一個(gè)特質(zhì) HasLegs:

class Animal
trait Furry extends Animal
trait HasLegs extends Animal
trait FourLegged extends HasLegs
class Cat extends Animal with Furry with FourLegged

類 Cat 的繼承層級(jí)和線性化次序展示在下圖。繼承次序使用傳統(tǒng)的 UML 標(biāo)注指明:白色箭頭表明繼承,箭頭指向超類型。黑色箭頭說明線性化次序,箭頭指向 super 調(diào)用解決的方向。


當(dāng)這些類和特質(zhì)中的任何一個(gè)通過super調(diào)用了方法,那么被調(diào)用的實(shí)現(xiàn)將是它線性化的右側(cè)的第一個(gè)實(shí)現(xiàn)。


參考資料

Scala學(xué)習(xí)——特質(zhì)

轉(zhuǎn)載請(qǐng)注明作者Jason Ding及其出處
GitCafe博客主頁(yè)(http://jasonding1354.gitcafe.io/)
Github博客主頁(yè)(http://jasonding1354.github.io/)
CSDN博客(http://blog.csdn.net/jasonding1354)
簡(jiǎn)書主頁(yè)(http://www.itdecent.cn/users/2bd9b48f6ea8/latest_articles)
Google搜索jasonding1354進(jìn)入我的博客主頁(yè)

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

  • 這篇講義只講scala的簡(jiǎn)單使用,目的是使各位新來的同事能夠首先看懂程序,因?yàn)?scala 有的語(yǔ)法對(duì)于之前使用習(xí)...
    MrRobot閱讀 3,040評(píng)論 0 10
  • 本章要點(diǎn) 類可以實(shí)現(xiàn)任意數(shù)量的特質(zhì) 特質(zhì)可以要求實(shí)現(xiàn)它們的類具備特定的字段、方法或超類 和Java接口不同,Sca...
    胡楊1015閱讀 823評(píng)論 0 0
  • 讀《快學(xué)Scala 》一書的摘要 Scala 運(yùn)行于JVM之上,擁有海量類庫(kù)和工具,兼顧函數(shù)式編程和面向?qū)ο蟆?在...
    abel_cao閱讀 1,380評(píng)論 0 8
  • 前情回顧:小日子平淡而溫馨,她在努力適應(yīng)已然安定下來開始一成不變的生活,他努力讓她感覺到安心和舒適以及滿滿的安全感...
    繞指安生閱讀 517評(píng)論 2 6
  • 下午忙完瑣事,躺在床上靜靜的看完了《絕戀》,看完之后很震撼,還有說不出的沉重。正好這個(gè)時(shí)候老公走進(jìn)房間,像孩子渴...
    阿蓮af閱讀 2,439評(píng)論 0 0

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