300 likes | 400 Views
Inheritance Techniques. Subcontracting Anchored Types. class C { ... void p(S s) { …; s.r(); … } } Call : C c; T t; c.p(t);. class S { … void r() { … }; } class T extends S { … void r() { … }; } Spec : pre-post conditions. C. S. T.
E N D
Inheritance Techniques Subcontracting Anchored Types L14IT
class C { ... void p(S s) { …; s.r(); … } } Call: C c; T t; c.p(t); class S { … void r() { … }; } class T extends S { … void r() { … }; } Spec: pre-post conditions C S T Inheritance is subcontracting. L14IT
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. L14IT
Odds and Ends • Global Inheritance Structure • 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.) L14IT
class Device feature alternate: Device; set_alternate(a:Device) is do alternate := a end end class Printer inherit Device feature alternate: Printer; set_alternate(a:Printer) is do alternate := a end end 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 end Anchored Declarations : Covariance L14IT
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. 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 item : G; left, right : like Current; put_right(n: like left) is do … end; put_left(n: like left) is do … end; ... end; Link BiLink L14IT
class Point { int x,y; boolean eq(Point p) { return ( x == p.x) && ( y == p.y) ; }} class ColoredPoint extends Point { Color c; boolean eq(Point p) { return ( x == p.x) && ( y == p.y) && ... ; }} // unsatisfactory class ColoredPoint extends Point { Color c; boolean eq(ColoredPoint p) { return ( super(p) && ( c == p.c ) ); }} // unsatisfactory override class ColoredPoint extends Point { Color c; boolean eq(Point p) { if (p instanceof ColoredPoint) return ( super(p) ) && ( c == p.c ) ; else return false; }} //redeclare L14IT
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.) L14IT
“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 L14IT
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. Typing problems L14IT
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. L14IT
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()); L14IT
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(); L14IT
Safe Change in C++ (Java 5, Cool) class Parent { public: Parent * clone () { return new Parent(); } }; class Child : public Parent { public: Child * clone () { return new Child(); } }; L14IT
Signature Rule for Function Subtyping class A { public RA m (PA p) ; } class B extends A { public RB m (PB p) ; } • RBmust be a subtype of RA: RB <= RA • PBmust be a supertype of PA: PB >=PA • covariant for results, contravariant for parameters L14IT
Implementation L14IT
class A { int a; } class C extends A { int d; } class B extends A { int b,c; } class D extends B { int e; } C a d B a b c Single Inheritance of Data Fields A a D a b c e L14IT
(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. L14IT
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(); } Class Descriptors for Dynamic Method Lookup L14IT
x x x x x y y A_f A_f D_f C_g C_g B_g A_f A B C D L14IT
(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. L14IT
class A { int a; } class B { int b,c; } class C extends A { int d; } class D extends A, B, C { int e; } A a D C a B a b c b d d c e Multiple Inheritance of Data Fields d L14IT
B b 1 a c 2 2 1 C 2 a 1 3 b 4 a c d 5 d 2 e Fields Offsets in Descriptors for Multiple Inheritance A 1 D L14IT
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(); int h(); } Object Layout and Class Descriptors for Dynamic Method Lookup L14IT
C++ Approach L14IT
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. L14IT
C++ vtables for D-object A; D D_f 0 l a B B_g 0 d b,c C D_h -d d e L14IT
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(); } (Minor Change) L14IT
C++ vtables for D-object A; D A_f 0 l a B D_g -l d b,c C C_h 0 d e L14IT
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. L14IT