面向对象语言概论(四)

类别:编程语言 点击:0 评论:0 推荐:

 

四,彻底划清界限(继续分离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] isend; 也似乎没有什么用。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协议。

 

(译者按,使用它对加强C++中的模板的类型安全会很有用。其实,对使用gp的程序来说,也许子协议约束比子类型约束更合理。子类型的sumbsumption要求多态,即dynamic dispatch。但很多时候,我们并不一定需要类型参数支持多态。请参看我的拙文:http://www.allaboutprogram.com/bb/viewtopic.php?t=255)

 

 

 

 

思考题:

1. Java是支持Covariant的Array的。你可以把一个String[] 类型的对象当作一个Object[]类型来使用。

那么,Java的covariant array是类型安全的吗?为什么?

2. 大家都知道经典的矩形和正方形之间的类型关系吧?在支持get, set的正方形和矩形之间,并不存在subtype的关系,这是因为subsumption对get或set操作是不安全的。但是,是否正方形和矩形之间就不可能有subtype的关系呢?如果矩形的类型只支持get, 结果会是什么?如果正方形只支持set, 结果又是什么呢?

 

下章预告:

基于对象的面向对象语言。

本文地址:http://com.8s8s.com/it/it29509.htm