[ Pobierz całość w formacie PDF ]
.However, we show the corresponding object type,IndDoubleNode-Type, in Figure 3.5.Even such an independent definition has problems.While we have not yet discussed the rules for determining when object typesare in the subtype relation, the code in Figure 3.5 can be used to show that theresulting type,IndDoubleNodeType, cannot be a subtype ofNodeType.The functionbreakitin the figure is well-typed, assetNexttakes aparameter of typeNodeType, and the expressionnew Nodeas the actualparameter creates a value of typeNodeType.SupposeIndDoubleNodeTypewere a subtype ofNodeType.If so, andifdnwas a value generated fromIndDoubleNode, thenbreakit(dn)would be well-typed, as values of typeIndDoubleNodeTypecould mas-queradeas elements of typeNodeType.However, that is not the case!The execution ofnode setNext(newNode)in the body ofbreakitwould result in the message send ofsetPrevto the parameternewNext.ButnewNextholds a value that is generated fromNode.This would resultin a run-time type error as elements generated fromNodehave nosetPrevmethod.This shows that object typeIndDoubleNodeTypecould not be3.2 Simple type systems are lacking in flexibility 45LglDbleNodeType = ObjectType {getValue:Void Integer;setValue:Integer Void;getNext:Void NodeType;setNext:NodeType Void;getPrev:Void LglDbleNodeType;setPrev:LglDbleNodeType Void}class LglDbleNode inherits Node modifies setNext {previous:LglDbleNodeType := nil;function getPrev(): LglDbleNodeType is{ return self.previous }function setPrev(newPrev:LglDbleNodeType): Void is{ self.previous := newPrev }function setNext(newNext:NodeType): Void is{ super setNext(newNext);((LglDbleNodeType)newNext) setPrev(self) }// cast necessary to recognize setPrev}Figure 3.4 Legal doubly linked node class with cast.a subtype ofNodeType, as elements of typeIndDoubleNodeTypecannotsafely masquerade as elements of typeNodeType.This example is particularly troubling in that it seems to be tailor-made forthe use of inheritance, but it (i) cannot be written correctly with an invarianttype system, and (ii) would result in a type error if the type of objects gener-ated from the desired subclass were a subtype of the type of objects generatedfrom the original class.These problems are not special to theNodeexample,but arise with all binary methods because of the desire for a covariant changein the parameter type of binary methods.We will later find a way to add expressiveness to languages in order toallow us to writeDoubleNodeas a subclass ofNode.We will then need to46 3 Type Problems in Object-Oriented LanguagesIndDoubleNodeType = ObjectType {getValue:Void Integer;setValue:Integer Void;getNext:Void IndDoubleNodeType;setNext:IndDoubleNodeType Void;getPrev:Void IndDoubleNodeType;setPrev:IndDoubleNodeType Void}function breakit(node:NodeType): Void is{ node setNext(new Node) }varn:NodeTypedn:IndDoubleNodeType{n := new Node;dn := new IndDoubleNode;breakit(n); // No problembreakit(dn) // Run-time error here!}Figure 3.5 Example showing whyIndDoubleNodeTypecannot be a subtype ofNodeType.ensure that the resulting object types are not subtypes in order to avoid thesecond problem.invariant type systems do have the desirable property that subclasses gen-erate subtypes, but it may be worth losing subtyping in some circumstancesif it makes it significantly easier to define desirable subclasses.We will eval-uate these trade-offs when we examine the more flexible type system of Eiffelin the next chapter.3.2.3 Other typing problemsIn both of the examples above, the difficulties arose from an attempt to keepreturn or parameter types the same as those of the object being defined.While this is an extremely important special case, there are other examples3.2 Simple type systems are lacking in flexibility 47class CircleClass {center:PointType := nil;.function getCenter(): PointType is{ return self.center }.}class ColorCircleClass inherits Circlemodifies getCenter {color:ColorType := black;.function getCenter(): ColorPtType is {.}// illegal type change in subclass!.}Figure 3.6 Circle and color circle classes.where it is desirable to change a type in a subclass in a covariant way.In theseexamples, the type to be changed may have no relation to the type of objectsgenerated by the classes being defined.Many examples of this phenomenonarise when we have objects with other objects as components.Figure 3.6 contains the definition of a class,CircleClass, with aget-Centermethod that returns a point.If we define a subclass that represents acolor circle, it would be reasonable to wish to redefinegetCenterto returna color point, as in the code in the figure.This would be illegal by the ruleson method types in these invariant object-oriented languages.We would liketo have a typing system that allows such changes, as long as they are typesafe.Again, however, even if we allowed a change in the return type ofget-Centerin the subclass, we still have the problem that we cannot changethe type of the instance variablecenter.Ifcenteris still of typePoint-Type, we will either need to add a type cast (which may fail) to the body ofgetCenteror we may have to change the body to build a new color pointfrom thecolorandcenterinstance variables.The latter involves a lotof work each timegetCenteris called, and is probably not worth the ef-48 3 Type Problems in Object-Oriented Languagesfort compared to separately returning the values ofcenter(as a point) andcolor.Finally, there likely will be a methodsetCenterinCircleClassthattakes a parameter of typePointType.Even C++ will not allow changingthe types of parameters of methods in subclasses, sosetCenterinColor-CircleClassmust accept parameters of typePointType [ Pobierz całość w formacie PDF ]