Eiffel: Analysis, Design and Programming Bertrand Meyer (Nadia Polikarpova) Chair of Software Engineering
Inheritance
3 Overview Basics Redefinition Polymorphism and dynamic binding Inheritance and contracts Deferred classes Polymorphic containers Inheritance and genericity: constrained genericity Multiple inheritance Non-conforming inheritance Covariance and anchored types
4 Extending the basic notion of class LIST_OF_ CARS BAG_OF_ CARS LINKED_LIST_ OF_CARS LIST_OF_ CITIES LIST_OF_ PERSONS Abstraction Specialization Type parameterization Genericity Inheritance
5 Inheritance basics Principle: Describe a new class as extension or specialization of an existing class (or several with multiple inheritance) If B inherits from A : As modules: all the services of A are available in B (possibly with a different implementation) As types: whenever an instance of A is required, an instance of B will be acceptable (“is-a” relationship)
6 Terminology If B inherits from A (by listing A in its inherit clause): B is an heir of A A is a parent of B For a class A: The descendants of A are A itself and (recursively) the descendants of A ’s heirs Proper descendants exclude A itself Reverse notions: Ancestor Proper ancestor More precise notion of instance: Direct instances of A Instances of A : the direct instances of A and its descendants (Other terminology: subclass, superclass, base class) B A C D E
7 What you can do to inherited features Effect (implicit) Redefine Undefine Rename Change export status Select
8 Example hierarchy center* perimeter* perimeter +... * deferred + effective ++ redefine d display* rotate* move* * FIGURE * OPEN_FIGURE * CLOSED_FIGURE SEGMENTPOLYLINE + ELLIPSE CIRCLE + POLYGON TRIANGLE SQUARE RECTANGLE perimeter + display + rotate + move + perimeter ++ side1 side2 diagonal perimeter ++ display + rotate + move + perimeter ++ display ++ rotate ++ move ++
9 Redefinition Class may redefine (“override”) inherited features Redefinition is explicit In line with Uniform Access principle, may redefine inherited function into attribute Not the other way around!
10 Redefinition 1: polygons class POLYGON inherit CLOSED_FIGURE create make feature vertex : ARRAY [POINT] vertex_count : INTEGER perimeter : REAL is -- Perimeter length do from... until... loop Result := Result + vertex [i ]. distance (vertex [i + 1])... end end invariant vertex_count >= 3 vertex_count = vertex.count end vertex [i ] vertex [i + 1]
11 Redefinition 2: rectangles class RECTANGLE inherit POLYGON redefine perimeter end create make feature diagonal, side1, side2 : REAL perimeter : REAL is -- Perimeter length do Result := 2 (side1 + side2) end invariant vertex_count = 4 end side1 side2 diagonal
12 Inheritance, typing and polymorphism (POLYGON) (RECTANGLE) p r Assume: p : POLYGON ; r : RECTANGLE ; t : TRIANGLE x : REAL Permitted: x := p. perimeter x := r. perimeter x := r. diagonal p := r NOT permitted: x := p. diagonal -- Even just after p := r ! r := p
13 Dynamic binding What is the effect of the following (if some_test is true)? if some_test then p := r else p := t end x := p. perimeter Redefinition: A class may change an inherited feature, as with POLYGON redefining perimeter. Polymorphism: p may have different forms at run-time. Dynamic binding: Effect of p. perimeter depends on run- time form of p.
14 Without dynamic binding? display (f : FIGURE ) do if “f is a CIRCLE” then... elseif “f is a POLYGON” then... end and similarly for all other routines! Tedious; must be changed whenever there’s a new figure type
15 With inheritance and associated techniques With: Initialize: and: Then just use: f : FIGURE c : CIRCLE p : POLYGON create c. make (...) create p. make (...) if... then f := c else f := p end f. move (...) f. rotate (...) f. display (...) -- and so on for every -- operation on f !
16 Binding Binding is the process in which A routine invocation f (a), x.f (a) is connected to code that is executed Accessing or writing to a variable a a := E is connected a memory location.
17 Static vs. dynamic binding If binding is done at compile time, this is called “Static Binding” using “Static Linking”. If binding is done at program load time, this is called “Static Binding” using “Dynamic Linking”. If binding is done at runtime this is called “Dynamic Binding”.
18 Static Binding in C In the.h header file: extern int foo (arg c); extern float my_variable; In the.c file: int foo (arg c); float my_variable;
19 Execution of a call in C On the caller side: Push the arguments on the stack Push the current PC + 1 on the stack (return address) Jump to the code to execute This is “filled out” during linking/binding Clean the stack
20 Execution of a call in C (cont.) On the callee side: Add some extra memory on the stack for the local variables Initialize the local variables (if necessary) Execute the implementation of the routine Read the return address from the stack Jump to the return address
21 Dynamic Binding in non-OO Dynamic binding is not exclusive for object-orientation: Lisp (1958) Forth (1970) - a mixture called “Threaded Code” C (1970) - Why ?
22 Dynamic Binding in C #include void hello () { printf ("Hello\n"); } void world () { printf ("World\n"); } int main (int argc, char **argv) { void (*func)(void); if (argc > 1) func = hello; else func = world; func (); /* Dynamic Binding */ }
23 Dynamic Binding and OO We need dynamic binding in object-orientation Identifiers used in program text are relative to the current object: Attributes are relative to Current Routines may be redefined in subclasses
24 Using class tables Class Table Pointer Integer Field Class A Pointer to feature f Pointer to feature g 1. Read first field from Current 2. Add second field from Current 3. Store result in third field of Current 4. Go to the second entry in Current's class table class A feature a,b,c : INTEGER f is do c := a + b g end g is... end
25 With inheritance Class Table Pointer Integer Field Class B Pointer to feature f Pointer to feature g Boolean Field Pointer to feature h A B class B inherit A redefine f feature d: BOOLEAN f is... h is... end
26 Contracts and inheritance Issue: what happens, under inheritance, to Class invariants? Routine preconditions and postconditions?
27 Invariants Invariant Inheritance rule: The invariant of a class automatically includes the invariant clauses from all its parents, “and”-ed. Accumulated result visible in flat and interface forms.
28 Contracts and inheritance r require ensure r require ensure a1 : A a1. r (…) … Correct call in C: if a1. then a1. r (...) -- Here a1. holds end r ++ CADB client of inherits from ++ redefinition
29 Assertion redeclaration rule When redeclaring a routine, we may only: Keep or weaken the precondition Keep or strengthen the postcondition
30 A simple language rule does the trick! Redefined version may have nothing (assertions kept by default), or require else new_pre ensure then new_post Resulting assertions are: original_precondition or new_pre original_postcondition and new_post Assertion redeclaration rule in Eiffel
31 The role of deferred classes Express abstract concepts independently of implementation Express common elements of various implementations Terminology: Effective = non-deferred (i.e. fully implemented)
32 In e.g. LIST: forth deferred end A deferred feature ensure index = old index + 1 require not after
33 Deferred! In the same class search (x : G) -- Move to first position after current -- where x appears, or after if none. do from until after or else item = x loop forth end “Programs with holes” Mixing deferred and effective features Effective!
34 “Don’t call us, we’ll call you!” A powerful form of reuse: The reusable element defines a general scheme Specific cases fill in the holes in that scheme Combine reuse with adaptation
35 Deferred classes in EiffelBase CONTAINER * BOX * COLLECTION * TRAVERSABLE * FINITE * INFINITE * BOUNDED * UNBOUNDED * COUNTABLE * RESIZABLE * BAG * SET * HIERARCHICAL * LINEAR * TABLE * ACTIVE * INTEGER_ INTERVAL * BILINEAR * INDEXABLE * CURSOR_ STRUCTURE * DISPENSER * SEQUENCE * ARRAYSTRINGHASH_TABLE STACK * QUEUE * … … * deferred
36 deferred class VAT inherit TANK feature in_valve, out_valve: VALVE fill is -- Fill the vat. require in_valve.open out_valve.closed deferred ensure in_valve.closed out_valve.closed is_full end empty, is_full, is_empty, gauge, maximum,... [Other features]... invariant is_full = (gauge >= 0.97 * maximum) and (gauge <= 1.03 * maximum) end Deferred classes for analysis
37 Polymorphic data structures class LIST [G] feature... last: G is... extend (x: G) is... end fl: LIST [FIGURE] r: RECTANGLE s: SQUARE t: TRIANGLE p: POLYGON... fl. extend (p); fl. extend (t); fl. extend (s); fl. extend (r) from fl.start until fl.after loop fl. item. display; fl.forth end (SQUARE) (RECTANGLE) (TRIANGLE) (POLYGON) fl
38 Figure hierarchy (reminder) center* perimeter* perimeter +... * deferred + effective ++ redefine d display* rotate* move* * FIGURE * OPEN_FIGURE * CLOSED_FIGURE SEGMENTPOLYLINE + ELLIPSE CIRCLE + POLYGON TRIANGLE SQUARE RECTANGLE perimeter + display + rotate + move + perimeter ++ side1 side2 diagonal perimeter ++ display + rotate + move + perimeter ++ display ++ rotate ++ move ++
39 Enforcing a type: the problem fl. store ("FILE_NAME") Two years later: fl := retrieved ("FILE_NAME“) x := fl. last-- [1] print (x. diagonal )-- [2] What’s wrong with this? If x is declared of type RECTANGLE, [1] is invalid. If x is declared of type FIGURE, [2] is invalid.
40 Object-Test Local Enforcing a type: the Object Test if attached {RECTANGLE } fl. last as r then print (r. diagonal ) … Do anything else with r, guaranteed to be non-void … and of dynamic type (descendant of) RECTANGLE else print ("Too bad.") end Expression to be tested SCOPE of the Object-Test Local Optional (if omitted just tests for void)
41 Earlier mechanism: assignment attempt f : FIGURE r : RECTANGLE... fl. retrieve ("FILE_NAME") f := fl. last r ?= f if r /= Void then print (r. diagonal ) else print ("Too bad.") end
42 Assignment attempt x ?= y with x : A Semantics: If y is attached to an object whose type conforms to A, perform normal reference assignment. Otherwise, make x void.
43 What we have seen Basics Redefinition Polymorphism and dynamic binding Inheritance and contracts Deferred classes Polymorphic containers
44 Topics for today Inheritance and genericity: constrained genericity Multiple inheritance Non-conforming inheritance Covariance and anchored types
45 Inheritance + Genericity Unconstrained genericity LIST [G] e.g. LIST [INTEGER], LIST [PERSON] Constrained genericity HASH_TABLE [G, H ―> HASHABLE ] VECTOR [G ―> NUMERIC ]
46 Constrained genericity class VECTOR [G ] feature plus alias "+" (other : VECTOR [G]): VECTOR [G] -- Sum of current vector and other. require lower = other. lower upper = other. upper local a, b, c: G do... See next... end... Other features... end
47 Adding two vectors + =uvw 1 2
48 Constrained genericity Body of plus alias "+": create Result. make (lower, upper) from i := lower until i > upper loop a := item (i ) b := other. item (i ) c := a + b-- Requires “+” operation on G! Result. put (c, i ) i := i + 1 end
49 The solution Declare class VECTOR as class VECTOR [G –> NUMERIC ] feature... The rest as before... end Class NUMERIC (from the Kernel Library) provides features plus alias "+", minus alias "-" and so on.
50 Improving the solution Make VECTOR itself a descendant of NUMERIC, effecting the corresponding features: class VECTOR [G –> NUMERIC ] inherit NUMERIC feature... Rest as before, including infix "+"... end Then it is possible to define v : VECTOR [INTEGER ] vv : VECTOR [VECTOR [INTEGER ]] vvv : VECTOR [VECTOR [VECTOR [INTEGER ]]]
51 In the end... Genericity is always constrained because LIST [G] is just an abbreviation of LIST [G -> ANY]
52 Combining abstractions Given the classes TRAIN_CAR, RESTAURANT how would you implement a DINER?
53 Examples of multiple inheritance Combining separate abstractions: Restaurant, train car Calculator, watch Plane, asset Home, vehicle Tram, bus
54 Composite figures
55 Multiple inheritance: Composite figures A composite figure Simple figures
56 Defining the notion of composite figure COMPOSITE_ FIGURE center display hide rotate move … count put remove … FIGURE LIST [FIGURE ]
57 In the overall structure COMPOSITE_ FIGURE FIGURE LIST [FIGURE ] OPEN_ FIGURE CLOSED_ FIGURE SEGMENTPOLYLINEPOLYGON ELLIPSE RECTANGLE SQUARE CIRCLE TRIANGLE perimeter + perimeter* perimeter ++ diagonal perimeter ++ perimeter +
58 A composite figure as a list Cursor item forth after before
59 Composite figures class COMPOSITE_FIGURE inherit FIGURE LIST [FIGURE] feature display -- Display each constituent figure in turn. do from start until after loop item. display forth end end... Similarly for move, rotate etc.... end Requires dynamic binding
60 Going one level of abstraction higher A simpler form of procedures display, move etc. can be obtained through the use of iterators Use agents for that purpose
61 Multiple inheritance: Combining abstractions COMPARABLE NUMERIC STRING COMPLEX INTEGER REAL, >=, … +, –, , / … (total order relation) (commutative ring)
62 The Java-C# solution No multiple inheritance for classes “Interfaces”: specification only (but no contracts) Similar to completely deferred classes (with no effective feature) A class may inherit from: At most one class Any number of interfaces
63 Multiple inheritance: Combining abstractions COMPARABLE NUMERIC STRING COMPLEX INTEGER REAL, >=, … +, –, , / … (total order relation) (commutative ring)
64 How do we write COMPARABLE? deferred class COMPARABLE [G ] feature end less alias "<" (x : COMPARABLE [G ]): BOOLEAN deferred end less_equal alias "<=" (x : COMPARABLE [G ]): BOOLEAN do Result := Current < x or Current ~ x end greater alias " >" (x : COMPARABLE [G ]): BOOLEAN do Result := x < Current end greater_equal alias " >=" (x : COMPARABLE [G ]): BOOLEAN do Result := x <= Current end
65 Lessons from this example Typical example of program with holes We need the full spectrum from fully abstract (fully deferred) to fully implemented classes Multiple inheritance is there to help us combine abstractions
66 Multiple inheritance: Name clashes f C f A B ?
67 Resolving name clashes f rename f as A_f C f A B A_f, f
68 Consequences of renaming a1 : A b1 : B c1 : C... c1. f c1. A_f a1. f b1. f rename f as A_f C f A B A_f, f f Invalid: a1. A_f b1. A_f
69 Are all name clashes bad? A name clash must be removed unless it is: Under repeated inheritance (i.e. not a real clash) Between features of which at most one is effective (i.e. others are deferred)
70 Feature merging ABC D f + f *f * f *f * Deferred + Effective
71 Feature merging: with different names ABC D h + g *g * f *f * Deferred + Effective Renaming g f h f class D inherit A rename g as f end B C rename h as f end feature... end
72 Feature merging: effective features ABC D f + Deferred + Effective -- Undefine f --
73 Undefinition deferred class T inherit S undefine v end feature... end
74 Merging through undefinition class D inherit A undefine f end B C undefine f end feature... end ABC D f + f -- Deferred + Effective -- Undefine
75 Merging effective features with different names A B C D h + f + g + f -- class D inherit A undefine f end B rename g as f undefine f end C rename h as f end feature... end h f g f
76 Acceptable name clashes If inherited features have all the same names, there is no harmful name clash if: They all have compatible signatures At most one of them is effective Semantics of such a case: Merge all features into one If there is an effective feature, it imposes its implementation
77 Feature merging: effective features a1: Ab1: Bc1: Cd1: D a1.gb1.fc1.hd1.f ABC D g+g+ f+f+h+h+ g f h f f-f-f-f-
78 A special case of multiple inheritance TEACHER STUDENT ASSISTANT UNIVERSITY _MEMBER id This is a case of repeated inheritance ?? ????
79 Indirect and direct repeated inheritance A D B C A D
80 Multiple is also repeated inheritance A typical case: copy ++ is_equal ++ copy is_equal ?? copy C_copy is_equal C_is_equal C LIST D ANY
81 Sharing and replication Features such as f, not renamed along any of the inheritance paths, will be shared. Features such as g, inherited under different names, will be replicated. A B C D fgfg g g_b g g_c
82 The need for select A potential ambiguity arises because of polymorphism and dynamic binding: a1 : ANY d1 : D … a1 := d1 a. copy (…) copy ++ is_equal ++ copy C_copy is_equal C_is_equal C LIST D copy is_equal ANY
83 Removing the ambiguity class D inherit LIST [T ] select copy, is_equal end C rename copy as C_copy, is_equal as C_is_equal,... end
84 When is a name clash acceptable? (Between n features of a class, all with the same name, immediate or inherited.) They must all have compatible signatures. If more than one is effective, they must all come from a common ancestor feature under repeated inheritance.
85 Another application of renaming Provide locally better adapted terminology. Example: child (TREE ); subwindow (WINDOW)
86 Multiple Inheritance and Class Tables Why does this not work with multiple subtyping ? Can we still do O(1) lookup ?
87 The dynamic binding function Name × StaticType × DynamicType → Code
88 The dynamic binding function (Name × StaticType) → DynamicType → Code
89 The dynamic binding function Static Dynamic (Name × StaticType) → DynamicType → Code
90 Feature Tables f of A g of A h of B g of C ABC Dynamic Type Static Type Code Pointer
91 Non-conforming inheritance class ARRAY [G] feature... lower, upper: INTEGER resize (l, u: INTEGER) ensure lower = l; upper = u end class ARRAYED_LIST [G] inherit LIST [G] ARRAY [G]... invariant starts_from_1: lower = 1 end a: ARRAY [INTEGER]; l: ARRAYED_LIST [INTEGER]... a := l a.resize (-10, 10) ARRAYED_LIST LIST ARRAY Class invariant violation
92 Inheritance basics: extended If B inherits from A : As modules: all the services of A are available in B (possibly with a different implementation) As types: whenever an instance of A is required, an instance of B will be acceptable (“is-a” relationship) If B inherits from A in a non-conforming way: As modules: all the services of A are available in B (possibly with a different implementation) No relationship between types!
93 Non-conforming inheritance class ARRAYED_LIST [G] inherit LIST [G] inherit {NONE} ARRAY [G]... invariant starts_from_1: lower = 1 end a: ARRAY [INTEGER]; l: ARRAYED_LIST [INTEGER]... a := l a.resize (-10, 10) ARRAYED_LIST LIST ARRAY Non-conforming inheritance Not allowed
94 No need for select count count capacity ARRAY LIST ARRAYED_LIST FINITE Potential ambiguity is resolved is favor of conforming parent: f : FINITE [...] al: ARRAYED_LIST [...] … f := al print (f. count) Version from LIST
95 Covariance class LIST [G] feature cursor: CURSOR go_to (c: CURSOR) is do … end … end class LINKED_LIST [G] inherit LIST [G] redefine cursor, go_to,... end feature cursor: LINKED_CURSOR go_to (c: LINKED_CURSOR) is do … end … end LINKED_LIST LIST LINKED_CURSOR CURSOR
96 Anchored types class LIST [G] feature cursor: CURSOR go_to (c: like cursor) is do … end … end class LINKED_LIST [G] inherit LIST [G] redefine cursor,... end feature cursor: LINKED_CURSOR -- No need to redefine `go_to’ … end
97 Semantics of anchored types In class C : x: SOME_TYPE y: like x In class C, y is treated exactly as with y: SOME_TYPE In a descendant D of C, if x has been redeclared to some other type, y will be treated as it if also had that type.
98 Type redefinition rule Eiffel: covariant redefinition of result (may change type to a descendant of the original type) covariant redefinition of arguments Traditional notion of subtyping : covariant redefinition of result contravariant redefinition of arguments (may change type to an ancestor of the original type) Contravariant redefinition: safe but useless
99 The problem with covariance list: LIST [...] linked_list: LINKED_LIST [...] c: CURSOR … list := linked_list list.go_to (c) class LIST feature cursor: CURSOR go_to (c: like cursor) is do … end … end class LINKED_LIST inherit LIST redefine cursor,... end feature cursor: LINKED_CURSOR -- No need to redefine `go_to’ … end Catcall!
100 CAT calls CAT stands for Changing Availability or Type A routine is a CAT if some redefinition changes its export status or the type of any of its arguments A call is a catcall if some redefinition of the routine would make it invalid because of a change of export status or argument type
101 Catcall cases Covariant redefinition of arguments Non-generic case Generic case Descendant hiding (in earlier versions of Eiffel)
102 Covariant redefinition: non-generic case class ANIMAL feature eat (a_food: FOOD) deferred end class WOLF inherit ANIMAL redefine eat end feature eat (a_meat: MEAT) do … end WOLF ANIMAL FOOD MEAT GRASS
103 animal: ANIMAL wolf: WOLF food: FOOD grass: GRASS create wolf create grass animal := wolf food := grass animal.eat (grass) Covariant redefinition: non-generic case WOLF ANIMAL FOOD MEAT GRASS
104 animal_list: LINKED_LIST [ANIMAL] sheep_list: LINKED_LIST [SHEEP] sheep: SHEEP wolf: WOLF sheep_list.extend (sheep) animal_list := sheep_list animal_list.extend (wolf) Covariant redefinition: generic case WOLF ANIMAL SHEEP
105 class LINKED_LIST [ANY] feature extend (v: ANY) do … end end class LINKED_LIST [SHEEP] feature extend (v: SHEEP) do … end end class LINKED_LIST [WOLF] feature extend (v: WOLF) do … end end Covariant redefinition: generic case
106 class RECTANGLE inherit POLYGON export {NONE} add_vertex end … invariant vertex_count = 4 end r: RECTANGLE; p: POLYGON... p := r p.add_vertex (...) Descendant hiding RECTANGLE POLYGON Earlier: catcall! Now: not allowed
107 class RECTANGLE inherit {NONE} POLYGON export {NONE} add_vertex end … invariant vertex_count = 4 end r: RECTANGLE; p: POLYGON... p := r p.add_vertex (...) Descendant hiding: solution 1 RECTANGLE POLYGON Not allowed Nonconforming inheritance
108 class POLYGON feature... variable_vertex_count: BOOLEAN do Result := True end add_vertex (...) require variable_vertex_count do... end end class RECTANGLE inherit POLYGON redefine variable_vertex_count end feature... variable_vertex_count: BOOLEAN = False end p: POLYGON... if p.variable_vertex_count then p.add_vertex (...) end Descendant hiding: solution 2 RECTANGLE POLYGON
109 Inheritance: summary (1) Type mechanism: lets you organize our data abstractions into taxonomies Module mechanism: lets you build new classes as extensions of existing ones Polymorphism: flexibility with type safety Dynamic binding: automatic adaptation of operation to target, for more modular software architectures Contracts inheritance: ensures properties to clients in presence of polymorphism Deferred classes: a mechanism to express properties of abstract concepts
110 Inheritance: summary (2) Polymorphic containers + object test: flexible and type- safe Constrained genericity: allows calling specific features on expression of parameter types Multiple inheritance: powerful way of combining abstractions Non-conforming inheritance: a way to express code reuse without subtyping Catcalls: result from covariant redefinition of argument types (even more tricky with genericity!)