Contents • What is genericity? • Alternatives: Genericity and its Implementation – multiple instantiation – casting • Checking correctness of generic types at Meyer Chapter 10, 16.4 definition: constrained genericity • Subtyping relations between instances • Implementation of genericity • Anchor Types 1 2 What is Genericity? Examples of Genericity • We don’t want to write a Stack for Integers, floats, • We have seen Stack[G]. employees, etc. • Vectors of numbers with addition and – More effort – Versions will become inconsistent, extra debugging effort ordering • Write once, use for many types • A map of (key, element) pairs • Implementations: – C++: templates • A binary search tree for types that have a ≤ – Eiffel: constrained genericity function – Java: not present, expected in Java 1.5 • Problem: different types allow for different operations. 3 4 Two Ideas The Alternative: Casting • Automatic multiple instantiation (C++) • How do we write a stack in Java? – No genericity (expected in 1.5, in 2004) • Automatic casting – Stack is a stack of Objects – Every push and pop uses a cast 5 6
Reminder: cast up Reminder: cast down • cast: compile-time construct to change • Cast down (narrowing): see an object as a subtype changes the static type (compile-time type) – No guarantee that it works of an object – Rectangle r = (Rectangle) f; //Java – Rectangle *r = dynamic_cast<Rectangle *> f; //C++ • Cast up (widening): See an object as if it – Check for legality at runtime: if incorrect, were of supertype. Always legal . • throw exception (Java) or • assign NULL (Eiffel, C++) – Figure f = (Figure) new Rectangle(); – Useful for such things as: “Print diameter of all or rectangles in list of figures” – Figure f = new Rectangle(); 7 8 Casting and Introspection Casting Instead of Genericity • Can check beforehand if a cast is legal Stack s; Circle c; Figure f; Rectangle r; s.push((Object) c); – In Java: s.push((Object) c); if(f instanceof Rectangle) f = (Figure) s.pop(); //legal narrowing r = (Rectangle) f; r = (Rectangle) s.pop(); //illegal narrowing else r = null; • Alternative to Genericity – In C++ (similar in Eiffel) if(Rectangle *r = dynamic_cast<Rectangle *> f) • Effectively disables static type checking in Java! OK • Errors caught during run time, not compile time. else f is not a rectangle 9 10 Checking Correctness: Vector Checking Correctness class Vector[G]{ G get(int i){...} put(int i, G item) {...} • Not all templates can be used with all types! infix “+”(other: Vector[G]): Vector[G]{ Vector[G] result; G item; require length = other.length; for (i = 0; i < length; length++){ item = item(i) + other.item(i); result.put(i, item); } return result; } } 11 12
Checking Correctness Correctness at Instantiation • How do we make sure a template is correct? • Instantiation: creation of code from a declaration Stack<Integer> or similar • Parameter may allow only certain • First method to check template correctness. Used in C++ operations: we can add numbers, not • When template is encountered – Check syntax employees – No type checks • Vector[Employee] is nonsense. • When template is used, check for correctness: – instantiate template by creating code as if it were a macro – check instantiated code • Drawback? • How do we make sure? 13 14 Correctness at Instantiation Correctness at Definition • Drawback: • Second method to check template correctness: The Eiffel Method – Have to tell user the constraints for the • Give argument a type, allow only instantiations of parameter correct type: Constrained Genericity – You never know if a template is “correct” • Classname[G -> H] instantiates generic type unless you have used it in all possible ways Classname, in which G must be subclass of H • Fits “strong typing” idea 15 16 Correctness at Definition Correctness at Definition class Vector[G -> Numeric ]{ G get(int i){...} put(int i, G item) {...} deferred class Numeric feature infix “+”, infix “-”, infix “*”, prefix Vector[G] plus(other: Vector[G]){ “-”, zero(), one() Vector[G] result; end G item; require length = other.length; • A ring: two groups, defined by (*,1) and for(i = 0; i < length; length++){ (+,0), the latter of which is commutative, item = item(i) + other.item(i); result.put(i, item); such that * distributes over +. } return result; 17 18 }
Correctness at Definition Correctness • class Sortable[G->Comparable]... In favor of correctness at In favor of correctness at instantiation definition • class Dictionary[G->Hashable, H] Flexibility: No need to Correctness clear when • class Stack[G] (= Stack[G -> Object] ) create classes that template written (good – Any type has ==, !=, clone, equal,... combine all needed for libraries) functionality and to derive from them Both have Correctness at compile time! 19 20 Subtyping Subtyping • If Truck is derived from Vehicle, is • If Truck is derived from Vehicle, is Stack[Car] derived from Stack[Vehicle]? Stack[Car] derived from Stack[Vehicle]? • No. (Sorry.) We could otherwise perform (code in Java): Stack[Car] cs = new Stack[Car](); Stack[Vehicle] vs = cs; Invalid code! vs.push(Truck t); Car c = cs.pop(); // would assign t to car • Sharing does not work, copying is OK: • We can write conversion operators that take Stack[Car] and yield a new object Stack[Vehicle]. 21 22 Implementation Multiple Instantiation • First implementation: C++ Method • The compiler perspective • Like macro expansion • Translating Java + templates to Java. How • For every type X if Stack[X] occurs in code, would you do it? recompile code for Stack[G] with X in place – Odersky and Wadler, Pizza into Java, translating theory into practice, Proc. Symposium on Principles of Programming Languages (POPL’97), of G 146– 159, 1997. – This is when error checks are done • Two alternatives • In place of means simple syntactic – Multiple Instantiation substitution – Casting 23 24
Casting: Translation Into Java Casting up and down Class Stack[G]{ push(G f){ ... } • Second implementation } • Classname[G -> H] is implemented by Stack[Figure] s; Classname , in which G is replaced by H (If no H Circle c; present, use Object .) Figure f; Rectangle r; • Every transfer from and to Classname is cast Object o; s.push(c); • At compile time, we can check that s.push(r); – Generic type definition is OK when instantiated with H s.push(o); – Class instantiations are correct (use subclass T of H ) f = s.pop(); o = s.pop(); c = s.pop(); 25 26 Casting: Translation Into Java Multiple Instantiation vs. Casting //compile code with Figure in place of G Class Stack{ Class Stack[G->Figure]{ Multiple instantiation: Casting push(Figure f){ ... } push(G f){ ... } } } • Takes more space for •Takes less space Stack s; // OK: Polygon Stack[Polygon] s; program text derived from Figure Triangle t; • No Time overhead Triangle t; •No Time overhead Polygon f; Polygon f; Rectangle r; Rectangle r; Note: Casting Object o; Object o; unnecessary: we can s.push((Figure) t); s.push(t); s.push((Figure) r); s.push(r); check correctness at // compile-time error! s.push(o); compile time! f = (Figure) s.pop(); f = s.pop(); o = (Object) s.pop(); o = s.pop(); //compile-time error t = s.pop(); 27 28 Final Problem: Anchor Types Anchor Types class Vector[G -> Numeric]{ deferred class Numeric • How do we complete the definition of G get(int i){...} feature put(int i, G item) {...} Numeric, so that it can be used for Vector? infix “+”, infix “-”, infix “*”, prefix “-”, zero(), Vector[G] plus(other: one() Vector[G]){ end Vector[G] result; G item; • A class C is numeric if it has a plus operator require length = other.length; that takes two arguments of type C and for(i = 0; i < length; length++){ returns a result of type C item = item(i) + other.item(i); result.put(i, item); } return result; } 29 30
Anchor Types Anchor Types deferred class Numeric class Vector[G -> Numeric]{ deferred class Numeric feature G get(int i){...} feature infix “+”(other: like Current): like Current infix “+”, infix “-”, put(int i, G item) {...} ... infix “*”, prefix “-”, end zero(), one() Vector[G] plus(other: end Vector[G]){ end Vector[G] result; G item; • Like current means “of same type as current instance”, e.g. First proposal: write require length = other.length; class Integer inherit Numeric feature infix “+”(other: for(i = 0; i < length; length++){ Numeric): Numeric{…} ... item = item(i) + end other.item(i); Won’t work vector[Real] cannot result.put(i, item); has method infix “+” with type Integer X Integer → contain arbitrary Numeric s ! } return result; Integer. } 31 32 Conclusions • Genericity useful if static typing is important (i.e., always) • Implement by multiple instantiation or casting • Check correctness at instantiation or at definition (constrained genericity) • Anchor types help define interfaces 33
Recommend
More recommend