Scala:abstract override

定義

官方文檔對于abstract override的定義是:

The override modifier has an additional significance when combined with the abstract modifier. That modifier combination is only allowed for value members of traits.
We call a member M of a template incomplete if it is either abstract (i.e. defined by a declaration), or it is labeled abstractand override and every member overridden by M is again incomplete.
Note that the abstract override modifier combination does not influence the concept whether a member is concrete or abstract. A member is abstract if only a declaration is given for it; it is concrete if a full definition is given.

這里面有三個要點:

  • 修飾符abstract override只能用于特質(zhì)(Trait)的成員

  • 稱類成員M是不完整的(incomplete)默伍,如果M是抽象的(如M只有聲明沒有定義),或者M(jìn)被abstract override修飾愤兵,同時任何被M重載的成員也同樣是不完整的粥诫。
    數(shù)學(xué)定義:令

    • overrided(x) 為被x重載的成員
    • 布爾函數(shù) abstract(x)為x是抽象的(即只有定義)
    • 布爾函數(shù) abstractOverride(x)為x是被abstract override
    • 布爾函數(shù) incomplete(x)為x是不完整的纠吴,


    incomplete(M)=abstract(M)∨[ abstractOverride(M)∧incomplete( overrided(M))]

  • abstract override并不影響成員是具體的還是抽象的川队。如果成員只有聲明則它就是抽象的殿怜,如果有具體定義廉涕,那它就是具體的命迈。

注意到M被abstract override修飾時,如果M重載的成員是具體的火的,那么M就不是 incomplete

class Father{
    def name="Father"
}
trait Son extends Father{
    abstract override def name="Son"
}
val son=new Son{}
son.name //"Son"

上面代碼中,特質(zhì)Son繼承了類Father淑倾,并且方法name被重載馏鹤。盡管nameabstract override修飾,但是因為name重載的方法(即Fathername)并不是 incomplete 娇哆,所以特質(zhì)Son的方法name也不是 incomplete湃累。
這同時也說明了,盡管nameabstract override修飾碍讨,但是因為有具體定義治力,所以它是具體的而不是抽象的。

另外注意到語句val son=new Son{}勃黍,Son是特質(zhì)不能被實例化宵统,但是此處寫法實際上類似于Java一樣聲明了一個匿名類,因為Son中沒有抽象成員覆获,所以花括號為空马澈。

多提一句,在官方文檔中abstractoverride定義分別是:

abstract
The abstract modifier is used in class definitions. It is redundant for traits, and mandatory for all other classes which have incomplete members. Abstract classes cannot be instantiated **with a constructor **invocation unless followed by mixins and/or a refinement which override all incomplete members of the class. Only abstract classes and traits can have abstract term members.

要點:

  • abstract只能修飾類弄息,成員不能使用abstract
  • 特質(zhì)沒有必要聲明abstract
  • 類中如果有不完整(incomplete)成員痊班,則必須聲明abstract
  • 抽象類不能實例化,除非被混入摹量,且/或 被使用匿名類實現(xiàn)
  • 只有抽象類或特質(zhì)才能有抽象成員

override
The override modifier applies to class member definitions or declarations. It is mandatory for member definitions or declarations that override some other concrete member definition in a parent class. If an override modifier is given, there must be at least one overridden member definition or declaration (either concrete or abstract).

要點:

  • 如果被重載的方法是父類中有具體定義的方法涤伐,則override是必須的馒胆,否則可省略(例如,實現(xiàn)特質(zhì)的抽象方法凝果,就沒有必要寫override
  • 如果用override修飾祝迂,則必然存在一個被重載成員的定義或聲明,無論是抽象還是定義

說明

abstract override應(yīng)該兼具abstractoverride兩種特性豆村。
首先液兽,如果特質(zhì)的超類中不存在M方法,則特質(zhì)中定義M方法時不能使用abstract override掌动,因為會違反override的定義四啰。

其次考慮下面的例子:

abstract class Animal{
    def bark    //abstract method
}
trait Dog extends Animal{
    abstract override def bark=println("WoooW!")  //abstract override
}
new Dog{}.bark 

error: object creation impossible, 
  since method bark in trait Dog of type => Unit 
  is marked `abstract' and `override', 
  but no concrete implementation could be found in a base class
       new Dog{}.bark
           ^

發(fā)現(xiàn),方法bark盡管有具體定義粗恢,但是編譯器無法創(chuàng)建匿名類對象柑晒,原因是“特質(zhì)中的方法barkabstract override修飾,但是無法在基類中找到具體的實現(xiàn)”眷射。從這一點上講匙赞,方法bark此時也具有抽象性(abstract)。

這是符合定義的妖碉,因為顯然Dog.barkabstract override修飾涌庭,且被重載方法Animal.barkincomplete ,從而 Dog.bark也是 incomplete的欧宜,自然會報錯坐榆,即:

??incomplete(Dog.bark)
=abstract(Dog.bark)∨[ abstractOverride(Dog.bark)∧incomplete( overrided(Dog.bark))]
=False∨[ Trueincomplete( Animal.bark)]
=False∨[ TrueTrue]
=True

為了解決這個問題,有兩種:
只要讓 被重載方法Animal.bark不是 incomplete即可(incomplete( overrided(Dog.bark))==False):

abstract class Animal{
    def bark=println("Emmm....")  //Now it's not incomplete
}
trait Dog extends Animal{
    //So method bark is also not incomplete
    abstract override def bark=println("WoooW!")
}
//the invocation makes sense
new Dog{}.bark //WoooW!

或者只要讓重載方法Dog.bark不被abstract override修飾即可( abstractOverride(Dog.bark)==False):

abstract class Animal{
    def bark    //abstract method
}
trait Dog extends Animal{
    override def bark=println("WoooW!")  //redundant modifier override
}
new Dog{}.bark  //WoooW!

以上只是極端情況的演示冗茸,但是實際上沒有必要這么做席镀。

結(jié)論

只有在滿足下面條件的情況下,我們才應(yīng)在trait 中定義的某一方法之前添加
abstract 關(guān)鍵字:該方法調(diào)用了super 對象中的另一個方法夏漱,但是被調(diào)用的
這個方法在該trait 的父類中尚未定義具體的實現(xiàn)方法豪诲。

考慮下面的樣板代碼:

abstract class AbstractSuper{
    def abstractMethod( Params ) : ReturnType
}

trait TraitSub extends AbstractSuper{
    def abstractMethod( Params ) :ReturnType ={  //implements the abstract method
        /**
        重載代碼實現(xiàn)
        */
    }
}

如果重載代碼實現(xiàn)邏輯中:

  • 不含有父類的 incomplete 方法:即沒有super.method,或邏輯中有super.method但是super.method不是 incomplete 方法挂绰,則直接實現(xiàn)即可屎篱,不必加override修飾符,并且可以使用new TraitSub{}.abstractMethod來引用扮授。
    注意
    • 這里的super.method指代父類中的任何 incomplete 方法芳室,比如super.abstractMethod或是其他什么的。
    • 所謂“父類具有 incomplete 方法”刹勃,要么該方法就是抽象的(沒有具體定義)堪侯,要么其有定義,但是在定義內(nèi)部包含了祖先類的 incomplete 方法荔仁,這是一個遞歸過程伍宦。
  • 含有父類的 incomplete 方法TraitSub.abstractMethod有具體定義芽死,但是具體定義中又有類似super.abstractMethodincomplete方法,這樣TraitSub.abstractMethod方法本身就不具體次洼,此時必須使用abstract override來修飾关贵,以此提醒編譯器(和讀者):盡管TraitSub.abstractMethod提供了方法體,但方法并沒有完全實現(xiàn)卖毁。

為什么abstract override只能用來修飾特質(zhì)的成員呢揖曾?假設(shè)可以用來修飾抽象類的方法,那么該方法本身就應(yīng)該使用了父抽象類的抽象方法super.abstractMethod亥啦,但是這在設(shè)計邏輯上是說不通的:父抽象類的抽象方法本就是交由子類來實現(xiàn)的炭剪,表示抽象的通用行為,子類卻又在實現(xiàn)邏輯內(nèi)部調(diào)用父抽象類的抽象方法翔脱,這是毫無道理的奴拦。在這里,super就是指代父抽象類

那么特質(zhì)為什么能反過來使用父類的抽象方法呢届吁,這似乎也在邏輯上說不通错妖?因為特質(zhì)有一個很特殊的性質(zhì):特質(zhì)中的super動態(tài)綁定的,你應(yīng)該注意到上面的兩類情況討論中疚沐,我并沒有使用AbstractSuper.abstractMethod的寫法而是寫成super.abstractMethod暂氯,也就是說,盡管TraitSub繼承了抽象類AbstractSuper亮蛔,但是它的super并不指代父抽象類AbstractSuper株旷,而是在運(yùn)行過程中動態(tài)綁定,所以在此之前都是不定的尔邓,是抽象的。

這樣的性質(zhì)使得特質(zhì)變得可堆疊锉矢√菟裕考慮一個抽象類A,其內(nèi)有抽象方法m沽损,有一個具體類C繼承了A并實現(xiàn)了方法m灯节,另外有一個特質(zhì)T也繼承了A,并且m的實現(xiàn)內(nèi)引用了super.m绵估,故被標(biāo)明abstract override炎疆。
現(xiàn)在假定有一個類P,它繼承了C国裳,設(shè)pP的實例形入,那么使用p.m實際就由C.m代理。現(xiàn)在缝左,將特質(zhì)T混入:class P extends C with T亿遂,那么使用p.m浓若,根據(jù)特質(zhì)的線性化(扁平化處理,類似python中的MRO)蛇数,它將由T.m進(jìn)行代理挪钓,而在其內(nèi)部的super.m實際上綁定為C.m,因此p.m=>T.m ( C.m )耳舅。
試想如果有很多特質(zhì)碌上,它們對方法m具有多態(tài)性,那么按照不同順序混入浦徊,super也就綁定不同的實例馏予,可能就展現(xiàn)為p.m=>T1.m ( T2.m( ....Tn.m (C.m ))... ),通過將特質(zhì)堆疊辑畦,使得方法變得更有選擇性和層次感吗蚌。

特質(zhì)的線性化順序

線性化算法
(1) 當(dāng)前實例的具體類型會被放到線性化后的首個元素位置處。
(2) 按照該實例父類型的順序從右到左的放置節(jié)點纯出,針對每個父類型執(zhí)行線性化算法蚯妇,并將執(zhí)行結(jié)果合并。(我們暫且不對AnyRef 和Any 類型進(jìn)行處理暂筝。)
(3) 按照從左到右的順序箩言,對類型節(jié)點進(jìn)行檢查,如果類型節(jié)點在該節(jié)點右邊出現(xiàn)過焕襟,那么便將該類型移除陨收。
(4) 在類型線性化層次結(jié)構(gòu)末尾處添加AnyRef 和Any 類型。
如果是對值類(如Int鸵赖、Short务漩、Double等)執(zhí)行線性化算法,請使用AnyVal 類型替代AnyRef 類型。

例如,有如下繼承關(guān)系(偽代碼):

class A
trait B (A)
trait C (A)
trait D (A)
trait E (C)
trait F (C)
class G (D,E,F,B)

對G進(jìn)行線性化:

  1. 當(dāng)前實例的具體類型會被放到線性化后的首個元素位置處悦荒。
    【方法鏈】:G
  2. 按照其父類型的順序從右到左的放置節(jié)點
    【方法鏈】:G B F E D
  3. 針對每個父類型執(zhí)行線性化算法
    【方法鏈】:
    G B F E D
    G B A F C E C D A
  4. 按照從左到右的順序,對類型節(jié)點進(jìn)行檢查居触,如果類型節(jié)點在該節(jié)點右邊出現(xiàn)過,那么便將該類型移除老赤。
    【方法鏈】:
    G B (A) F (C) E C D A
    G B ????? F ????? E C D A

因此對G的線性化即(從左至右為):GBFECDA轮洋。方法鏈(super的綁定)也按照這個順序進(jìn)行。顯然抬旺,根據(jù)特質(zhì)不同的混入順序弊予,這個方法鏈也會不同。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末开财,一起剝皮案震驚了整個濱河市块促,隨后出現(xiàn)的幾起案子荣堰,更是在濱河造成了極大的恐慌,老刑警劉巖竭翠,帶你破解...
    沈念sama閱讀 212,542評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件振坚,死亡現(xiàn)場離奇詭異,居然都是意外死亡斋扰,警方通過查閱死者的電腦和手機(jī)渡八,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來传货,“玉大人屎鳍,你說我怎么就攤上這事∥试#” “怎么了逮壁?”我有些...
    開封第一講書人閱讀 158,021評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粮宛。 經(jīng)常有香客問我窥淆,道長,這世上最難降的妖魔是什么巍杈? 我笑而不...
    開封第一講書人閱讀 56,682評論 1 284
  • 正文 為了忘掉前任忧饭,我火速辦了婚禮,結(jié)果婚禮上筷畦,老公的妹妹穿的比我還像新娘词裤。我一直安慰自己,他們只是感情好鳖宾,可當(dāng)我...
    茶點故事閱讀 65,792評論 6 386
  • 文/花漫 我一把揭開白布吼砂。 她就那樣靜靜地躺著,像睡著了一般鼎文。 火紅的嫁衣襯著肌膚如雪帅刊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評論 1 291
  • 那天漂问,我揣著相機(jī)與錄音,去河邊找鬼女揭。 笑死蚤假,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的吧兔。 我是一名探鬼主播磷仰,決...
    沈念sama閱讀 39,107評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼境蔼!你這毒婦竟也來了灶平?” 一聲冷哼從身側(cè)響起伺通,我...
    開封第一講書人閱讀 37,845評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎逢享,沒想到半個月后罐监,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡瞒爬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,612評論 2 327
  • 正文 我和宋清朗相戀三年弓柱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侧但。...
    茶點故事閱讀 38,747評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡矢空,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出禀横,到底是詐尸還是另有隱情屁药,我是刑警寧澤,帶...
    沈念sama閱讀 34,441評論 4 333
  • 正文 年R本政府宣布柏锄,位于F島的核電站酿箭,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏绢彤。R本人自食惡果不足惜七问,卻給世界環(huán)境...
    茶點故事閱讀 40,072評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望茫舶。 院中可真熱鬧械巡,春花似錦、人聲如沸饶氏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疹启。三九已至古程,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間喊崖,已是汗流浹背挣磨。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留荤懂,地道東北人茁裙。 一個月前我還...
    沈念sama閱讀 46,545評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像节仿,于是被迫代替她去往敵國和親晤锥。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,658評論 2 350

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法矾瘾,內(nèi)部類的語法女轿,繼承相關(guān)的語法,異常的語法壕翩,線程的語...
    子非魚_t_閱讀 31,602評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理蛉迹,服務(wù)發(fā)現(xiàn),斷路器戈泼,智...
    卡卡羅2017閱讀 134,637評論 18 139
  • 這兩年總看到有孩子不堪壓力而自殺的新聞大猛,大部分是來自學(xué)習(xí)上的壓力扭倾,年紀(jì)輕輕就結(jié)束了自己的生命,令人唏噓挽绩。生命是那樣...
    紅塵紫陌閱讀 1,144評論 8 13
  • 趙薇曾在網(wǎng)上po了一張最新的證件照,并附言“很友善唠亚,符合我的審美链方! ”,看來趙導(dǎo)對這次的照片很滿意灶搜。那么問題來了祟蚀,...
    樂像閱讀 337評論 0 0
  • 午覺起揭夢窗, 樹下重泉撲面割卖, 碧巖鳥前紛紛前酿。 不忍卒行,遂觀仁繼畫禪鹏溯。 三杯以后罢维,神態(tài)緩削, 水流沙河丙挽,隱隱秋聲...
    摩羯星一號閱讀 430評論 3 2