Scala概述(六)合成(2)

 

成員(Membership

如前所示,Iter 類從StringIteratorRichIterator 同時繼承了類成員(members )。簡單而言,一個類從以混入合成方式繼承Cn with … with C1 ,將會繼承其中所有類的成員,同時還可以自定義新的成員。由於Scala 保留了JavaC# 的靜態重載機制,因此可能從父類繼承同名的方法,也可以再定義同名的方法[1] 。爲了判斷類C 的一個方法到底是覆蓋父類中的同名方法,還是這兩個方法並存——即重載的關係,Scala 採用了匹配(matching )法,這也是從JavaC# 中類似的概念衍生來的:簡單地說,如果兩個類成員同名,並且具有相同的參數類型(如果兩個都是方法),就稱之爲相匹配。

一個類的成員總共兩種類型:具體和抽象的,每種類型分別對應一個判定規則:(注:下面幾段是說明Scala 語言判斷類成員屬性的內部機制,不關心這方面細節的可以忽略)

一個類C 的具體成員是指其或其父類的所有具體聲明M ,除非在其某個父類(也就是在L(C) )中已有一個匹配的具體成員。

一個類C 的抽象成員是指其或其父類的所有抽象聲明M ,除非在C 中已有一個匹配的具體成員,或者其某個父類(也就是在L(C) )中有一個匹配的抽象成員。

這些規則同樣決定了一個類C 與其父類之間匹配成員的覆蓋關係。首先,具體成員一定覆蓋抽象成員。其次,如果MM’ 同爲具體成員或抽象成員,且MC 的全序化當中出現在M’ 之前,則M 覆蓋M’

 

父類調用(Super Calls

我們考慮設計一個同步迭代器,也就是其操作在多線程之間是互斥的。

trait SyncIterator[T] extends AbsIterator[T] {

abstract override def hasNext: boolean =

synchronized(super .hasNext)

abstract override def next: T =

synchronized(super .next)

}

想要構造一個針對StringRich 、同步迭代器,可以用這三個類進行合成:

StringIterator(someString) with RichIterator[char]

with SyncIterator[char]

這個合成類從SynchIterator 繼承了hasNextNext ,這兩個方法都是對其父類的相應方法調用加了一個sychronized() 包裝。

由於RichIteratorSyncIterator 定義的方法相互不重合(注:原文是RichIteratorStringIterator ,應該有誤),因此它們出現在mixin 中的順序沒有影響,即上例寫成這樣也是等價的:

StringIterator(someString) with SyncIterator[char]

with RichIterator[char]

 

但是,這裏有一個小細節要注意:在SyncIterator 中的super 這個調用並不是靜態地綁定到其父類AbsIterator 上,因爲顯然這是毫無意義的,AbsIterator 定義的nexthasNext 都是抽象方法。實際上,這個super 調用實際上指向這個mixin 合成中的superclassStringIterator 的相應方法。從這個意義上講,一個mixin 合成的superclass 覆蓋了其各個mixin 當中靜態聲明的超類。這也就意味着super 調用在一個類當中無法被靜態解析,必須延遲到一個類被實例化或被繼承的時候才能解析出來。這一概念有如下精確定義:

假設CD 的父類,在C 當中的表達式super .M 應該能夠靜態解析爲C 的某個父類當中的成員M ,這樣才能保證類型正確。而在D 的語境中(context ,我將其翻譯爲語境,而不是通常人們翻譯的“上下文”。這個問題說來話長,有機會的話會變成一篇文章甚至一本書——譯者在這裏順便販賣一下私貨),這個表達式應該表示一個與M 相匹配的M’ ,這個成員應該在D 的全序當中位於C 之後的某個類裏定義。

最後注意一點:在JavaC# 等語言中,上述SyncIterator 當中的這種super 調用明顯是不合法的,因爲它會被指派爲父類當中的抽象成員(方法)。如同我們在上面看到的,這種構造在scala 中是合法的,只要保證一個前提,那就是這個類所出現的語境當中,其super 調用所訪問的父類成員必須是具體定義了的。這一點是由SyncIterator 當中的abstractoverride 這兩個關鍵字保證的。在scala 中,abstract override 這兩個關鍵字成對出現在方法定義中,表明這個方法並沒有獲得完全的定義,因爲它覆蓋(並使用)了其父類當中的抽象成員。一個類如果有非完整定義的成員,它自身必須是抽象類,其子類必須將這些非完整定義的成員重新定義,才能進行實例化。

super 的調用可以是級聯的,因此要遵從類的全序化(這是 Scala 的混入合成方式與多重繼承方式之間最主要的差異)。例如,考慮另一個與 SyncIterator 類似的類,它將其返回的每個元素都打印到標準輸出上:

trait LoggedIterator[T] extends AbsIterator[T] {

abstract override def next: T = {

val x = super .next; System.out.println(x); x

}

}

我們可以將這兩種迭代子( sychronized logged )通過 mixin 組合在一起:

class Iter2 extends StringIterator(someString)

with SyncIterator[char]

with LoggedIterator[char]

在這裏, Iter2 的全序化是:

{ Iter2, LoggedIterator, SyncIterator,

StringIterator, AbsIterator, AnyRef, Any }

這樣一來, Iter2 next 方法繼承自 LoggedIterator ,而該方法中的 super.next 則指向 SyncIterator next 方法,而後者當中的 super.next 則最終引用 StringIterator next 方法。

如果想對記錄日誌的動作進行同步,僅需要把兩個 mixin 的順序反過來即可實現:

class Iter2 extends StringIterator(someString)

with LoggedIterator[char]

with SyncIterator[char]

無論哪種情況, Iter2 next 方法當中 super 的調用都遵循其全序當中的父類順序。



[1] 有人可能反對這種設計方式,認爲這樣太複雜,但是爲了保證互操作性,這樣做是必須的,例如一個 Scala 類繼承一個 Java Swing 類的時候。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章