四,徹底劃清界限(繼續分離Subclassing和Subtyping)
在第二節我們討論了部分分離Subclassing和subtyping的方法,即subclassing-implies-subtyping. 現今的許多面向對象語言,如Java, C#都是采用了這種技術。除此之外,還有一種進一步分離Subclassing和subtyping的方法。這種被稱作inheritance-is-not-subtyping的方法通過完全割裂subclassing和subtyping之間的聯系而在更大程度上方便了代碼的重用。
它的產生很大程度上是由於人們想要使用在反協變位置上的Self類型 (如Self類型的參數)。當然,增大繼承的能力的代價是subsumption的靈活性降低了。當Self類型出現在反協變的位置上時,subclass不再意味著subtype, 因此,subsumption也就不存在了。
下面請考慮這樣兩個類型:
ObjectType Max is
var n: Integer;
method max(other:Max): Max;
end;
ObjectType MinMax is
var n: Integer;
method max(other:MinMax): MinMax;
method min(other:MinMax): MinMax;
end;
再考慮兩個類:
class MaxClass is
var n:Integer :=0;
method max(other: Self): Self is
if self.n > other.n then return self else return other end;
end;
end;
subclass MinMaxClass of MaxClass is
method min(other: Self): Self is
if self.n < other.n then return self else return other end;
end;
end;
方法min和max是二元的,因為它操作兩個對象:self和other. other的類型是一個出現在反協變位置上的Self類型。
注意,方法max有一個反協變的參數類型Self, 並且它被從類MaxClass繼承到了MinMaxClass.
很直觀地,類MaxClass對應著類型Max;類MinMaxClass對應著類型MinMax. 為了精確地表示這種對應關系,我們必須針對包含使用Self類型的成員的類重新定義ObjectTypeOf,以便得到ObjectTypeOf(MaxClass) = Max, ObjectTypeOf(MinMaxClass) = MinMax。
為了使以上的等式成立,我們把類中的Self類型映射到ObjectType中的類型名稱本身。我們同時讓Self類型在繼承的時候特化。
在本例中,當我們映射MinMaxClass的類型時,我們把繼承來的max方法中的Self類型映射到MinMax類型。而對MaxClass中max方法的Self類型,我們使用Max類型。
如此,我們可以得到,任何MaxClass生成的對象,都具備Max類型。而任何MinMaxClass生成的對象都具備MinMax類型。
雖然MinMaxClass是MaxClass的子類,但這裡MinMax卻不是Max的子類型(subtype).
舉個例子,如果我們假設subtype在這種情況下成立,那麼,對以下的這個類:
subclass MinMaxClass’ of MinMaxClass is
override max(other: Self): Self is
if other.min(self) = other then return self else return other end;
end;
end;
根據我們對Self類型的映射規則和基於結構的subtype規則,我們知道,ObjectTypeOf(MinMaxClass’) = MinMax, 所以,對任何MinMaxClass’生成的對象mm’ ,我們可以知道mm’ : MinMax.
而如果MinMax <: Max成立,根據subsumption, 我們就能推出mm’ : Max.
於是當我們調用mm’.max(m)的時候,m可以是任何Max類型的對象。但是,當max的方法體調用other.min(self)的時候,如果這個other不具有min方法,這個方法就會失敗。
由此可見,MinMax <: Max並不成立。
子類(subclass) 在使用反協變的Self類型時就不再具有subtype的性質了。
五,對象協議 (Object Protocol)
從上一節的討論,我們看到對使用反協變Self類型的類,subclass不再是subtype了。這是一個令人失望的結果,畢竟很多激動人心的面向對象的優點是通過subtype, subsumption來實現的。
不過,幸運的是,雖然失去了subtype, 我們還是可以從中挖掘出來一些可以作為補償的有用的東西的。只不過,不象subtype, 我們不能享受subsumption了。
下面就讓我們來研究這種新的關系。
在第四節的MinMax的例子中,subtype不再成立;簡單地使用泛型,引入
ObjectOperator P[M <: Max] is … end; 也似乎沒有什麼用。P[Max]雖然成立,但P[MinMax]卻是不合法的,因為MinMax <: Max不成立。
但是,直觀上看,任何支持MinMax這種協議的對象,也支持Max協議的 (雖然我們還不知道這個“協議”到底是個什麼東西)。於是,似乎隱隱約約地又一個叫做“子協議”(subprotocol)的家伙在向我們招手了。
為了發現這個子協議的關系,讓我們先定義兩個type operator (還記得嗎?就是作用在類型上的函數):
ObjectOperator MaxProtocol[X] is
var n: Integer;
method max(other: X) :X;
end;
ObjectOperator MinMaxProtocol[X] is
var n:Integer;
method max(other: X):X;
method min(other: X):X;
end;
這樣,Max = MaxProtocol[Max], MinMax = MinMaxProtocol[MinMax]
更一般地說,我們可以定義:
什麼 = 什麼-Protocol[什麼]
還記得lamda-calculus裡的fixpoint嗎?給定一個函數F, F(fixpoint(F)) = fixpoint(F)
而在我們這個子協議的type operator裡,如果我們認為type operator是作用於類型的函數的話, 那麼這個“什麼”,就是“什麼-Protocol”函數的fixpoint啊!
也就是說:
什麼= fixpoint (什麼-Protocol).
除了以上的fixpoint的性質,我們還發現了存在於Max和MinMax之間的關系。
首先,MinMax是MaxProtocol的一個post-fixpoint,即:
MinMax <: MaxProtocol[MinMax]
其次,我們可以看出:
MinMaxProtocol[Max] <: MaxProtocol[Max]
MinMaxProtocol[MinMax] <: MaxProtocol[MinMax]
最後,如果我們用<::來表示一個更高階的子類型關系:
P <:: P’ 當且僅當 P[T] <: P’[T]
那麼,MinMaxProtocol <:: MaxProtocol.
對於子協議的定義,我們可以采取上面的<::的定義,即:
如果S-Protocol<::T-Protocol, 那麼我們稱類型S和類型T之間是子協議關系。 (1)
我們也可以不用這個高階的關系,仍然使用<:這個subtype的關系:
如果S<:T-Protocol[S], 那麼我們稱類型S和類型T之間是子協議關系。 (2)
其實,第一個定義似乎更直觀一點,它更明確地顯示出子協議關系是作用於類型上的函數(type operator)之間的關系,而不是類型之間的關系。
使用泛型技術,如果我們的某一個類型需要一個實現MaxProtocol的類型來實例化的話,我們可以采用下面兩種方法中的一種:
ObjectOperator P1[X <: MaxProtocol[X]] is … end; (1)
ObjectOperator P2[P <:: MaxProtocol] is … end; (2)
這兩種方法在表達能力上是相同的。第一種方法叫做F-bounded parameterization. (譯者按,Generic Java據說就采用了這個方法);第二種方法叫做 higher-order bounded parameterization.
對於具體語言的實現,為了方便,我們可以隱藏這個type operator. 語法上可以直接對類型支持subprotocol的關系(用<#來表示)。
對我們的MinMax的例子來說,我們就有:
MinMax <# Max.
(譯者按,繞了一大圈,什麼fixpoint啊,post-fixpoint啊,什麼高階關系啦,希望沒把你繞暈。其實,你只需要記住MinMax <# Max, 就八九不離十了,呵呵)
<#這個關系並不具有subsumption的性質,所以,你不能指望從它身上得到傳統OO裡面的多態。但是,與泛型相結合,它卻是非常有用的。
比如說,我們可以對我們的所有你用來當作參數傳給我的基於GP的快速排序模板函數給出這樣的約束:用來實例化這個模板函數的的類型必須支持Comparable協議。