定義
官方文檔對于abstract override
的定義是:
The
override
modifier has an additional significance when combined with theabstract
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 labeledabstract
andoverride
and every member overridden by M is again incomplete.
Note that theabstract 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
被重載馏鹤。盡管name
被abstract override
修飾,但是因為name
重載的方法(即Father
的name
)并不是 incomplete 娇哆,所以特質(zhì)Son
的方法name
也不是 incomplete湃累。
這同時也說明了,盡管name
被abstract override
修飾碍讨,但是因為有具體定義治力,所以它是具體的而不是抽象的。
另外注意到語句val son=new Son{}
勃黍,Son
是特質(zhì)不能被實例化宵统,但是此處寫法實際上類似于Java一樣聲明了一個匿名類,因為Son
中沒有抽象成員覆获,所以花括號為空马澈。
多提一句,在官方文檔中abstract和override定義分別是:
abstract
Theabstract
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)該兼具abstract
和override
兩種特性豆村。
首先液兽,如果特質(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ì)中的方法bark
被abstract override
修飾,但是無法在基類中找到具體的實現(xiàn)”眷射。從這一點上講匙赞,方法bark
此時也具有抽象性(abstract
)。
這是符合定義的妖碉,因為顯然Dog.bark
被abstract override
修飾涌庭,且被重載方法Animal.bark
是 incomplete ,從而 Dog.bark
也是 incomplete的欧宜,自然會報錯坐榆,即:
??incomplete(
Dog.bark
)
=abstract(Dog.bark
)∨[ abstractOverride(Dog.bark
)∧incomplete( overrided(Dog.bark
))]
=False∨[ True∧incomplete(Animal.bark
)]
=False∨[ True∧True]
=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.abstractMethod
的incomplete方法,這樣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è)p
是P
的實例形入,那么使用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)行線性化:
- 當(dāng)前實例的具體類型會被放到線性化后的首個元素位置處悦荒。
【方法鏈】:G - 按照其父類型的順序從右到左的放置節(jié)點
【方法鏈】:G B F E D - 針對每個父類型執(zhí)行線性化算法
【方法鏈】:
G B F E D
G B A F C E C D A - 按照從左到右的順序,對類型節(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ì)不同的混入順序弊予,這個方法鏈也會不同。