$14.2 POLYMORPHISM 469 Assume,with the inheritance structure shown in the figure,the following declarations using short but mnemonic entity names: p:POLYGON;r:RECTANGLE:1:TRIANGLE Then the following assignments are valid: p:=r p :=1 These instructions assign to an entity denoting a polygon the value of an entity denoting a rectangle in the first case,a triangle in the second. Such assignments,in which the type of the source(the right-hand side)is different from the type of the target(the left-hand side),are called polymorphic assignments.An entity such as p which appears in some polymorphic assignment is a polymorphic entity Before the introduction of inheritance,all our assignments were monomorphic(non- polymorphic):we could assign-in the various examples ofearlier chapters-a point to a point,a book to a book,an account to an account.With polymorphism,we are starting to see more action on the attachment scene. The polymorphic assignments taken as example are legitimate:the inheritance structure permits us to view an instance of RECTANGLE or TRIANGLE as an instance of POLYGON.We say that the type of the source conforms to the type of the target.In the reverse direction,as withr=p,the assignment would not be valid.This fundamental type rule will be discussed in more detail shortly. Instead of an assignment,you may achieve polymorphism through argument passing, as with a call of the form f(r)or f(t)and a feature declaration of the form f(p:POLYGON)is do...end As you will remember,assignment and argument passing have the same semantics, and are together called attachment;we can talk of polymorphic attachment when the source and target have different types. What exactly happens during a polymorphic attachment? All the entities appearing in the preceding cases of polymorphic attachment are of reference types:the possible values for p,r and t are not objects but references to objects. So the effect of an assignment such as p:=r is simply to reattach a reference: O1 (before)op (POLYGON) (after) 02 Polymorphic reference reattachment (RECTANGLE)
§14.2 POLYMORPHISM 469 Assume, with the inheritance structure shown in the figure, the following declarations using short but mnemonic entity names: p: POLYGON; r: RECTANGLE; t: TRIANGLE Then the following assignments are valid: p := r p := t These instructions assign to an entity denoting a polygon the value of an entity denoting a rectangle in the first case, a triangle in the second. Such assignments, in which the type of the source (the right-hand side) is different from the type of the target (the left-hand side), are called polymorphic assignments. An entity such as p which appears in some polymorphic assignment is a polymorphic entity. Before the introduction of inheritance, all our assignments were monomorphic (nonpolymorphic): we could assign — in the various examples of earlier chapters — a point to a point, a book to a book, an account to an account. With polymorphism, we are starting to see more action on the attachment scene. The polymorphic assignments taken as example are legitimate: the inheritance structure permits us to view an instance of RECTANGLE or TRIANGLE as an instance of POLYGON. We say that the type of the source conforms to the type of the target. In the reverse direction, as with r := p, the assignment would not be valid. This fundamental type rule will be discussed in more detail shortly. Instead of an assignment, you may achieve polymorphism through argument passing, as with a call of the form f (r) or f (t) and a feature declaration of the form f (p: POLYGON) is do … end As you will remember, assignment and argument passing have the same semantics, and are together called attachment; we can talk of polymorphic attachment when the source and target have different types. What exactly happens during a polymorphic attachment? All the entities appearing in the preceding cases of polymorphic attachment are of reference types: the possible values for p, r and t are not objects but references to objects. So the effect of an assignment such as p := r is simply to reattach a reference: Polymorphic reference reattachment (POLYGON) O1 p r (RECTANGLE) O2 (after) (before) ✄
470 INTRODUCTION TO INHERITANCE $14.2 So in spite of the name you should not imagine,when thinking of polymorphism, some run-time transmutation of objects.Once created,an object never changes its type. Only references do so by getting reattached to objects of different types.This also means that polymorphism does not carry any efficiency penalty;a reference reattachment-a very fast operation-costs the same regardless of the objects involved. Polymorphic attachments willonly be permitted for targets of a reference type-not See"COMPOSITE for the other case,expanded types.Since a descendant class may introduce new attributes, OBJECTS AND EXPANDED TYPES". the corresponding instances may have more fields;the last figure suggested this by 87.page254. showing the RECTANGLE object bigger than the POLYGON object.Such differences in object size do not cause any problem if all we are reattaching is a reference.But if instead of a reference p is of an expanded type (being for example declared as expanded POLYGON),then the value of p is directly an object,and any assignment to p would overwrite the contents of that object.No polymorphism is possible in that case. Polymorphic data structures Consider an array of polygons: poly_arr:ARRAY [POLYGON When you assign a value x to an element of the array,as in poly arr.put (x,some index) (for some valid integer index value some index),the specification of class ARRAY indicates that the assigned value's type must conform to the actual generic parameter: class ARRAY [G]creation This is extracted from class ARRAY as it appears on page feature--Element change 373. put(v:G;i:INTEGER)is --Assign v to the entry of index i end -class ARRAY Because v,the formal argument corresponding to x,is declared of type G in the class, and the actual generic parameter corresponding to G is POLYGON in the case of poly arr, the type ofx must conform to POLYGON.As we have seen,this does not require x to be of type POLYGON:any descendant of POLYGON is acceptable. So assuming that the array has bounds I and 4,that we have declared some entities as p:POLYGON;r:RECTANGLE;s:SOUARE:1:TRIANGLE and created the corresponding objects,we may execute poly arr.put (p,1) poly_arr.put (r,2) poly_arr.put (s,3) poly _arr.put (1,4) yielding an array of references to objects of different types:
470 INTRODUCTION TO INHERITANCE §14.2 So in spite of the name you should not imagine, when thinking of polymorphism, some run-time transmutation of objects. Once created, an object never changes its type. Only references do so by getting reattached to objects of different types. This also means that polymorphism does not carry any efficiency penalty; a reference reattachment — a very fast operation — costs the same regardless of the objects involved. Polymorphic attachments will only be permitted for targets of a reference type — not for the other case, expanded types. Since a descendant class may introduce new attributes, the corresponding instances may have more fields; the last figure suggested this by showing the RECTANGLE object bigger than the POLYGON object. Such differences in object size do not cause any problem if all we are reattaching is a reference. But if instead of a reference p is of an expanded type (being for example declared as expanded POLYGON), then the value of p is directly an object, and any assignment to p would overwrite the contents of that object. No polymorphism is possible in that case. Polymorphic data structures Consider an array of polygons: poly_arr: ARRAY [POLYGON] When you assign a value x to an element of the array, as in poly_arr ● put (x, some_index) (for some valid integer index value some_index), the specification of class ARRAY indicates that the assigned value’s type must conform to the actual generic parameter: class ARRAY [G] creation … feature -- Element change put (v: G; i: INTEGER) is -- Assign v to the entry of index i … end -- class ARRAY Because v, the formal argument corresponding to x, is declared of type G in the class, and the actual generic parameter corresponding to G is POLYGON in the case of poly_arr, the type of x must conform to POLYGON. As we have seen, this does not require x to be of type POLYGON: any descendant of POLYGON is acceptable. So assuming that the array has bounds 1 and 4, that we have declared some entities as p: POLYGON; r: RECTANGLE; s: SQUARE; t: TRIANGLE and created the corresponding objects, we may execute poly_arr ● put (p, 1) poly_arr ● put (r, 2) poly_arr ● put (s, 3) poly_arr ● put (t, 4) yielding an array of references to objects of different types: See “COMPOSITE OBJECTS AND EXPANDED TYPES”, 8.7, page 254. This is extracted from class ARRAY as it appears on page 373
$14.2 POLYMORPHISM 471 A polymorphic array TRIANGLE) 3 (SQUARE) (RECTANGLE) (POLYGON The graphical objects have been represented by the corresponding geometrical shapes rather than the usual multi-field object diagrams. Such a data structure,containing objects of different types(all of them descendants of a common type),are called polymorphic data structures.We will encounter many examples in later discussions.The use ofarrays is just one possibility;any other container structure,such as a list or stack,can be polymorphic in the same way. The introduction of polymorphic data structures achieves the aim,stated at the beginning of chapter 10,of combining genericity and inheritance for maximum flexibility and safety.It is worth recalling the figure that illustrated the idea: Dimensions of Abstraction generalization SET [BOOK (See page 317.) Type parameterization Type parameterization LIST [PERSON LIST [BOOK] LIST [JOURNAL LINKED LIST [BOOK Specialization Types that were informally called SET OF BOOKS and the like on the earlier figure have been replaced with generically derived types,such as SET [BOOK] This combination of genericity and inheritance is powerful.It enables you to describe object structures that are as general as you like,but no more.For example:
§14.2 POLYMORPHISM 471 The graphical objects have been represented by the corresponding geometrical shapes rather than the usual multi-field object diagrams. Such a data structure, containing objects of different types (all of them descendants of a common type), are called polymorphic data structures. We will encounter many examples in later discussions. The use of arrays is just one possibility; any other container structure, such as a list or stack, can be polymorphic in the same way. The introduction of polymorphic data structures achieves the aim, stated at the beginning of chapter 10, of combining genericity and inheritance for maximum flexibility and safety. It is worth recalling the figure that illustrated the idea: Types that were informally called SET_OF_BOOKS and the like on the earlier figure have been replaced with generically derived types, such as SET [BOOK]. This combination of genericity and inheritance is powerful. It enables you to describe object structures that are as general as you like, but no more. For example: (POLYGON) (RECTANGLE) (SQUARE) (TRIANGLE) 1 3 4 2 A polymorphic array LIST [PERSON] LIST [BOOK] LIST [JOURNAL] SET [BOOK] LINKED_LIST [BOOK] Abstraction Specialization Type parameterization Type parameterization Dimensions of generalization (See page 317.)
472 INTRODUCTION TO INHERITANCE $14.3 .L/ST [RECTANGLE may contain squares,but not triangles. LIST [POLYGON]:may contain squares,rectangles,triangles,but not circles. LIST [F/GURE]:may contain instances of any of the classes in the FIGURE hierarchy,but not books or bank accounts. LIST [ANY]:may contain objects of arbitrary types. The last case uses class ANY,which by convention is an ancestor to all classes. We will study ANY in “Universal classes, By choosing as actual generic parameter a class at a varying place in the hierarchy, page 580. you can set the limits of what your container will accept. 14.3 TYPING FOR INHERITANCE That the remarkable flexibility provided by inheritance does not come at the expense of reliability follows from the use of a statically typed approach,in which we guarantee at compile time that no incorrect run-time type combination can occur. Type consistency Inheritance is consistent with the type system.The basic rules are easy to explain on the above example.Assume the following declarations: p:POLYGON r:RECTANGLE referring to the earlier inheritance hierarchy,of which the relevant extract is this: extent display* barycenter* FIGURE rotate* perimeter POLYGON diagonal QUADRANGLE perimeter++ sidel,side2 RECTANGLE
472 INTRODUCTION TO INHERITANCE §14.3 • LIST [RECTANGLE]: may contain squares, but not triangles. • LIST [POLYGON]: may contain squares, rectangles, triangles, but not circles. • LIST [FIGURE]: may contain instances of any of the classes in the FIGURE hierarchy, but not books or bank accounts. • LIST [ANY]: may contain objects of arbitrary types. The last case uses class ANY, which by convention is an ancestor to all classes. By choosing as actual generic parameter a class at a varying place in the hierarchy, you can set the limits of what your container will accept. 14.3 TYPING FOR INHERITANCE That the remarkable flexibility provided by inheritance does not come at the expense of reliability follows from the use of a statically typed approach, in which we guarantee at compile time that no incorrect run-time type combination can occur. Type consistency Inheritance is consistent with the type system. The basic rules are easy to explain on the above example. Assume the following declarations: p: POLYGON r: RECTANGLE referring to the earlier inheritance hierarchy, of which the relevant extract is this: We will study ANY in “Universal classes”, page 580. POLYGON QUADRANGLE perimeter+ diagonal RECTANGLE perimeter++ side1, side2 display* rotate* extent* … barycenter* … FIGURE ∗
$14.3 TY PING FOR INHERITANCE 473 Then the following are valid: p.perimeter:no problem,since perimeter is defined for polygons. p.vertices,p.translate (...)p.rotate (...with valid arguments. r.diagonal,r.sidel,r.side2:the three features considered are declared at the RECTANGLE or OUADRANGLE level. r.vertices,r.translate (...)r.rotate (...)the features considered are declared at the POLYGON level or above,and so are applicable to rectangles,which inherit all polygon features. r.perimeter:same case as the previous one.The version of the function to be called here is the redefinition given in RECTANGLE,not the original in POLYGON The following feature calls,however,are illegal since the features considered are not available at the poly gon level: p.sidel p.side2 p.diagonal These cases all result from the first fundamental typing rule: Feature Call rule In a feature call x.f,where the type ofx is based on a class C,feature fmust be defined in one of the ancestors of C. Recall that the ancestors of C include C itself.The phrasing"where the type of x is based on a class C"is a reminder that a type may involve more than just a class name if the class is generic:LINKED LIST [INTEGER]is a class type"based on"the class name LINKED LIST;the generic parameters play no part in this rule. Like all other validity rules reviewed in this book,the Feature Call rule is static;this means that it can be checked on the sole basis of a system's text,rather than through run- time controls.The compiler (which typically is the tool performing such checking)will reject classes containing invalid feature calls.If we succeed in defining a set of tight-proof type rules,there will be no risk,once a system has been compiled,that its execution will ever apply a feature to an object that is not equipped to handle it. Chapter 17 dis- Static typing is one of object technology's main resources for achieving the goal of cusses typing. software reliability,introduced in the first chapter of this book. It has already been mentioned that not all approaches to object-oriented software construction are statically typed;the best-known representative of dynamically typed languages is Smalltalk,which has no static Feature Call rule but will let an execution terminate abnormally in the case of a "message not understood"run-time error.The chapter on typing will compare the various approaches further
§14.3 TYPING FOR INHERITANCE 473 Then the following are valid: • p ● perimeter: no problem, since perimeter is defined for polygons. • p ● vertices, p ● translate (…), p ● rotate (…) with valid arguments. • r ● diagonal, r ● side1, r ● side2: the three features considered are declared at the RECTANGLE or QUADRANGLE level. • r ● vertices, r ● translate (…), r ● rotate (…): the features considered are declared at the POLYGON level or above, and so are applicable to rectangles, which inherit all polygon features. • r ● perimeter: same case as the previous one. The version of the function to be called here is the redefinition given in RECTANGLE, not the original in POLYGON. The following feature calls, however, are illegal since the features considered are not available at the polygon level: p ● side1 p ● side2 p ● diagonal These cases all result from the first fundamental typing rule: Recall that the ancestors of C include C itself. The phrasing “where the type of x is based on a class C ” is a reminder that a type may involve more than just a class name if the class is generic: LINKED_LIST [INTEGER] is a class type “based on” the class name LINKED_LIST; the generic parameters play no part in this rule. Like all other validity rules reviewed in this book, the Feature Call rule is static; this means that it can be checked on the sole basis of a system’s text, rather than through runtime controls. The compiler (which typically is the tool performing such checking) will reject classes containing invalid feature calls. If we succeed in defining a set of tight-proof type rules, there will be no risk, once a system has been compiled, that its execution will ever apply a feature to an object that is not equipped to handle it. Static typing is one of object technology’s main resources for achieving the goal of software reliability, introduced in the first chapter of this book. It has already been mentioned that not all approaches to object-oriented software construction are statically typed; the best-known representative of dynamically typed languages is Smalltalk, which has no static Feature Call rule but will let an execution terminate abnormally in the case of a “message not understood” run-time error. The chapter on typing will compare the various approaches further. Feature Call rule In a feature call x ● f, where the type of x is based on a class C, feature f must be defined in one of the ancestors of C. Chapter 17 discusses typing