Download presentation
Presentation is loading. Please wait.
1
Inheritance Techniques
Subcontracting Anchored Types ceg860 (Prasad) L14IT
2
Inheritance is subcontracting.
class C { ... void p(S s) { …; s.r(); … } Call: C c; T t; c.p(t); class S { … void r() { … }; } class T extends S { … Spec: pre-post conditions ceg860 (Prasad) L14IT
3
Assertion Redeclaration Rule
Parent Invariant Rule The invariants of all the parents (and therefore, the ancestors) of a class apply to the class itself. Assertion Redeclaration Rule A routine redeclaration may only replace the original precondition by one equal or weaker, and the original postcondition by one equal or stronger. { P } S.r() { Q } { P*} T.r() { Q*} P => P* and Q* => Q (Cf. Rule of consequence) The postcondition associated with a 0-ary routine redeclared as an attribute manifests itself as an additional class invariant. ceg860 (Prasad) L14IT
4
Odds and Ends Global Inheritance Structure Frozen Features
Java has a tree-structured class hierarchy (with null-type treated separately). The features of universal interest (such as equal(_), clone(), toString(), “locks for concurrency”, etc) are in class java.lang.Object. Frozen Features In Java, final can be applied to a class, a method, and a field to prohibit extension, redefinition, and change, respectively. (This is for security reasons, for freezing the semantics, and for defining a constant, respectively.) Null type cannot be the “bottom” of the hierarchy because multiple inheritance is prohibited. In Java, class Object supports wait(), notify(), …, etc and compensates partially for the lack of meta-classes. final class: security related. final method: standard copy, standard clone etc. If the meaning of a method is fixed in terms of certain other methods. Call-optimization by static binding or in-lining possible. final field: Color constants. ceg860 (Prasad) L14IT
5
Anchored Declarations : Covariance
class Device feature alternate: Device; set_alternate(a:Device) is do alternate := a end class Printer inherit Device feature alternate: Printer; set_alternate(a:Printer) Usually, redeclaration uses same signature to introduce or substitute a new algorithm. Eiffel also supports redeclaration by type specialization. class Device feature alternate: like Current; set_alternate (a:like Current) is do alternate := a end In effect, the following generates a compile-time error. Printer p; Device d; p.set_alternate(d); In Java, in the context of overloading, specificity is used for disambiguation. But the same does not hold vis a vis overriding. ceg860 (Prasad) L14IT
6
Type Redeclaration Rule
class Link[G] feature item : G; right : Link[G]; put_right(n:Link[G]) is do … end; ... end; class BiLink[G] inherit Link[G] feature left, right : like Current; put_right(n: like left) put_left(n: like left) Link BiLink Type Redeclaration Rule A redeclaration of a feature may replace the type of the feature, or the type of a formal, by any type that conforms to the original. This is a syntactic and purely static rule, with no effect on run-time objects. Essentially, this technique allows us to create homogeneous structures. A Link can be connected to a Link, a BiLink can be connected to a BiLink. The anchored construct allows one to make the redefinition more specific, enabling tighter compile-time checks. ceg860 (Prasad) L14IT
7
class Point { int x,y; boolean eq(Point p) { return ( x == p.x)
class ColoredPoint extends Point { Color c; boolean eq(ColoredPoint p) { return ( super(p) && ( c == p.c ) ); }} // unsatisfactory override boolean eq(Point p) { if (p instanceof ColoredPoint) return ( super(p) ) && ( c == p.c ) ; else return false; }} // redeclare class Point { int x,y; boolean eq(Point p) { return ( x == p.x) && ( y == p.y) ; }} class ColoredPoint extends Point { Color c; && ( y == p.y) && ... ; }} // unsatisfactory Simulation in Java of the effect of anchored declaration. The type mismatch is caught at run-time, not at compile-time. The effect of anchored declaration in Eiffel can be understood in terms of virtual code duplication at compile-time. ceg860 (Prasad) L14IT
8
Static Typing Type violation: x.f(args)
There is no f applicable to object associated with x, or args is not “acceptable”. A language is statically typed if it is equipped with consistency rules, enforceable by a compiler, whose observance by the software text guarantees that no execution of the system can cause a type violation. (E.g., Eiffel, Ada, ML, etc.) A language is strongly typed if it can guarantee the absence of type violations. (E.g., Java, Smalltalk, etc.) Java does most of the type checking at compile-time. However, it is not statically typed because it can throw exceptions such as ClassCastException. Polygon p; … Rectangle r = (Rectangle) p; Equivalent Eiffel’s assignment attempt is: r := p; if (r != void) { …} ceg860 (Prasad) L14IT
9
Benefits : Reliability, Readability, Efficiency
“Nature of the beast”: Trying to guarantee that no computation will ever fail forces one to disallow computations that might succeed. (E.g, n : integer = 2.0; is illegal in Ada.) Benefits : Reliability, Readability, Efficiency Typing vs Binding Typing: When do we know for sure that at run-time there will be an operation corresponding to f and applicable to the object attached to x (with the argument arg). Polymorphism Binding: Which operation will the call execute? Redeclaration Static typing - Static binding : Ada Static typing - Dynamic binding : Eiffel, approx. Java Dynamic typing - Static binding : ?? Assembly, Scripting ?? Dynamic typing - Dynamic binding : Smalltalk, Scheme ceg860 (Prasad) L14IT
10
Typing problems Interaction with
polymorphism (Covariance) Device d = new CD-Drive(); Printer p = new Printer(); d.set_alternate(p); Anchored declaration does not prevent type violation, but Java encoding seems to work. Interaction between redeclaration and descendant hiding Java prohibits method redeclaration that reduce visibility (e.g., from public to private). O/w, it can always be “beaten” by promoting subclass object and using dynamic binding. Java encoding of set_alternate in class CD-Drive will check that the incoming device is a CD-Drive at run-time. This operation is invoked by dynamic binding. Eiffel bans polymorphism for covariant redefinition. Can use generics instead. (In Cool, the parameter cannot be declared with type “SELF_TYPE”) Another “Cool” example: Device p = new Printer(); d.set_alternate(p); ceg860 (Prasad) L14IT
11
Covariance and Contravariance
If we could override a method changing the formal types or return types, then … A change that moves down the inheritance hierarchy, making it more specific, is said to be covariant. A change that moves up the inheritance hierarchy is said to be contravariant. ceg860 (Prasad) L14IT
12
Subtyping (substituitivity) Problem
class Parent { void test (covar : Mammal, contravar : Mammal) : boolean } class Child extends Parent { void test (covar : Cat, contravar : Animal) : boolean Parent aValue = new Child(); aValue.test(new Dog(), new Mammal()); Covariance problem ceg860 (Prasad) L14IT
13
Contravariance Problem
class Parent { Mammal test ( ) { return new Cat(); } class Child extends Parent { Animal test () { return new Bird(); Parent aValue = new Child(); Mammal result = aValue.test(); ceg860 (Prasad) L14IT
14
Safe Change in C++ (Java 5, Cool)
class Parent { public: Parent * clone () { return new Parent(); } }; class Child : public Parent { Child * clone () { return new Child(); } ceg860 (Prasad) L14IT
15
Signature Rule for Function Subtyping
class A { public RA m (PA p) ; } class B extends A { public RB m (PB p) ; } RB must be a subtype of RA: RB <= RA PB must be a supertype of PA: PB >=PA covariant for results, contravariant for parameters ceg860 (Prasad) L14IT
16
Implementation ceg860 (Prasad) L14IT
17
Single Inheritance of Data Fields
class A { int a; } class C extends A { int d; class B extends A { int b,c; class D extends B { int e; A B a b c a D a C a d b When B extends A, the fields of B that are inherited from A are laid out in a B record at the beginning, in the same order as they appear in A records. This is needed for code reuse of inherited methods in the context of polymorphism. And all this can be computed at compile-time. Similar issues for methods enables dynamic binding. c e ceg860 (Prasad) L14IT
18
(cont’d) Observe that for reusing inherited binary code of a parent method, it is necessary that the layout of the child object be an extension of the layout of the parent object. That is, the common fields have the same index in the two object layouts. this pointer is unchanged in a polymorphic assignment. ceg860 (Prasad) L14IT
19
Class Descriptors for Dynamic Method Lookup
class A { int x; int f(); } class B extends A { int g(); class C extends B { int g(); } class D extends C { int y; int f(); ceg860 (Prasad) L14IT
20
x x x x y x y A_f B_g A_f C_g D_f C_g A_f A B C D To execute c.f():
Each class descriptor has a sequence of fields and a sequence of applicable methods. Static Methods: To compile c.f(), the compiler searches in class C for method f. If not found, it searches through the parent class, … If f is found in some ancestor A, the call is compiled to A_f. Dynamic Methods: For instance methods, the class descriptor must contain a vector of method pointers, arranged as methods from Object to methods in C, similarly to data fields. When a subclass method overrides a class method, the method pointer is suitably changed, while preserving the index of the method in method table. To execute c.f(): 1. Fetch class descriptor d at offset 0 from object c. 2. Fetch method pointer from f offset of d. 3. Jump to p, saving return address. A B C D ceg860 (Prasad) L14IT
21
(cont’d) Observe that for the polymorphism to work correctly, a call to a parent method should invoke code for the child’s overriding method. This can be accomplished by having the index of the (overridden) parent method be the same as the index of the (overriding) child method. That is, the common methods have the same index in the method table. this pointer is unchanged in a polymorphic assignment. ceg860 (Prasad) L14IT
22
Multiple Inheritance of Data Fields
class A { int a; } class B { int b,c; class C extends A { int d; class D extends A, B, C { int e; B b c A a D a b c d e d C a Global analysis is necessary to compute same offset for each field name that can be used in every record containing that field. This is required to guarantee that polymorphism work correctly, that is, one can run an inherited instance method on all subclass instances. (This is relatively straightforward for single inheritance case.) d ceg860 (Prasad) L14IT
23
Fields Offsets in Descriptors for Multiple Inheritance
B 2 1 A a c b 2 1 D a b c d e 1 2 3 4 5 C 1 d a 2 Instead of having holes in the object, a more efficient approach is to have the holes in the class descriptor that maps the offsets to the instance fields stored compactly. Now each data fetch requires three (not one) instructions: 1. Fetch the descriptor pointer. 2. Fetch field offset value. 3. Fetch the data at the appropriate offset. Method look-up is done similarly. Each method is associated with a function pointer. All these information can be obtained using a pointer from an instance to its class record. ceg860 (Prasad) L14IT
24
Object Layout and Class Descriptors for Dynamic Method Lookup
class A { int a; int f(); } class B { int b, c; int g(); class C { int d; int h(); } class D extends A, B, C { int e; int f(); ceg860 (Prasad) L14IT
25
C++ Approach ceg860 (Prasad) L14IT
26
For memory efficient layout of instance fields and convenient reuse of inherited method code, the sub-objects of D corresponding to B and to C should resemble a B and a C instance respectively. (Resemblance to an A and a D instance is automatic.) This requires introducing method table reference slot, and adjusting this pointer for coercion/casting. ceg860 (Prasad) L14IT
27
C++ vtables for D-object
A; D D_f l a B B_g d b,c C D_h -d 1. A and D sub-object start at 0-offset, B-sub-object starts at l offset, and C-object at d-offset. 2. A aObj = new D(); aObj.f() invokes D_f. 3. B bObj = new D(); Coercion: Adjusts this to this+l. bObj.g() invokes B_g. 4. C cObj = new C(); cObj.h() invokes C_h (no this adjustment). 5. C cObj = (D*) bObj; Coercion: Adjusts this to this+d-l. cObj.h() invokes D_h after readjusting this to this-d. Note that 4 and 5 together contribute to dynamic binding table. Call : Type -> [Code, Pointer Adjustment] d e ceg860 (Prasad) L14IT
28
(Minor Change) class A { int a; int f(); } class B { int b, c;
int g(); class C { int d; int h(); } class D extends A, B, C { int e; int g(); ceg860 (Prasad) L14IT
29
C++ vtables for D-object
A; D A_f l a B D_g -l d b,c C C_h this correction = declaring class offset - defining class offset d e ceg860 (Prasad) L14IT
30
Dynamic Linking Global analysis is not feasible if a class can be loaded at run-time (such as in Java). Instead, it requires incremental approach. Hash table may be used in the class descriptor to map field names to offsets, and method names to method pointers. instanceof and dynamic type checks can be implemented efficiently using a display of pointers to ancestor class descriptors. ceg860 (Prasad) L14IT
Similar presentations
© 2025 SlidePlayer.com. Inc.
All rights reserved.