Genericity Parameterizing by Type 1 Generic Class • One that is parameterized by type » Works when feature semantics is common to a set of types • On object declaration the parameter is assigned a type » For example catalogue : ARRAY [ PERSON ] » We want an array of references to persons » All the array operations for catalogue are customized to use persons 2 Common Generic Classes • Collection classes – classes that are collections of objects » Strong typing requires specifying a type » But feature semantics is independent of type • Examples » Sets, Stacks, Arrays, Queues, Sequences a : ARRAY [ MATRIX_ELEMENT ] a : ARRAY [ INTEGER ] a : ARRAY [ STACK [ ELEPHANTS ] ] 3 Your Generic Classes • You can write generic classes • Why is this useful? » reuse » reliability 4 Understanding Genericity • We need » lists of INTEGER, lists of BOOK, lists of STRING etc. • Without genericity we have » INTEGER_LIST, BOOK_LIST, STRING_LIST • Problem — Reusability. » The basic operations (e.g. extend)are essentially the same. » Have to re-write essentially the same code over and over again. » Violation of the Single Choice Principle — changing STACK requires multiple changes, instead of a change at a single point. 5 Generic Stack class STACK [ G ] feature count : INTEGER -- number of elements empty : BOOLEAN is do ... end full : BOOLEAN is do ... end item : G is do ... end put ( x : G ) is do ... end remove is do ... end end -- STACK • Can use parameter G wherever a type is expected 6 Generic Array class ARRAY [ P ] creation make feature make ( minIndex , maxIndex : INTEGER ) is do ... end lower, upper, count : INTEGER put ( value : P ; index : INTEGER ) is do ... end infix "@" , item ( index : INTEGER ) : P is do ... end end -- ARRAY 7 Using the Generic Array circus : ARRAY [ STACK [ ELEPHANTS ] ] create circus.make ( 1 , 200 ) st_el : STACK [ ELEPHANTS ] -- element to put in the array create st_el circus.put ( st_el , 30 ) -- put an element into the array st_el2 : STACK [ ELEPHANTS ] st_el2 := circus @ 101 -- get an element from the array 8 Types of Genericity • Types » Unconstrained » Constrained • The previous examples showed unconstrained genericity » Any type could be passed as a parameter 9 Constrained Genericity • Used when the generic type parameters must satisfy some conditions • The following makes sense only if P has the feature ≥ class VECTOR [ P ] feature ... minimum ( x , y : P ) : P is do if x ≥ y then Result := y How we enforce else constraints is Result := x discussed in end Inheritance Techniques ... end 10 Discussion on Genericity • What programming languages offer genericity that you know of? Java? C++? Other? • C++ has the template: Set < int > s ; • Java has no genericity – can it be faked? • What is the effect of genericity on » compile time » size of the generated code » execution time » execution space • Warning: generics cheap in Eiffel – expensive in C++ 11 Java Stack public class Stack { private int count; private int capacity; private int capacityIncrement; private Object[] itemArray; // instead of ARRAY[G] Stack() { //constructor limited to class name count= 0; capacity= 10; capacityIncrement= 5; itemArray= new Object[capacity]; // no pre/postconditions/invariants } public boolean empty() { return (count == 0); } 12 Java Stack (cont.) public void push(Object x) { if(count == capacity) { capacity += capacityIncrement; Object[] tempArray= new Object[capacity]; for(int i=0; i < count; i++) tempArray[i]= itemArray[i]; itemArray= tempArray; } itemArray[count++]= x; } public Object pop() { if(count == 0) // no preconditions; // hence defensive programming return null; else return itemArray[--count]; } 13 Java Stack (cont.) public Object peek() { if(count == 0) return null; else return itemArray[count-1]; } } // end Stack; no class invariant public class Point { public int x; public int y; } 14 Java Stack runtime error class testStack { public static void main(String[] args) { Stack s = new Stack(); Point upperRight = new Point(); upperRight.x= 1280; // breaks information hiding upperRight.y= 1024; // cannot guarantee any contract s.push("red"); s.push("green"); // push some String s.push("blue"); // objects on the stack s.push(upperRight); // push a POINT object on the stack while(!s.empty()) { // cast all items from OBJECT String color = (String) s.pop(); // to STRING in order to print System.out.println(color); }}} // causes a run-time error in Java // ClassCastException: Point at testStack.main(testStack.java:18) // In Eiffel it is a compile time error 15 Does run-time vs. compile time matter? • Principle: When flying a plane, run-time is too late to find out that you don’t have landing gear! • Always better to catch errors at compile time! • This is the main purpose of Strong Typing [OOSC2, Chapter 17]. • Genericity helps to enforce Strong Typing, i.e. no runtime typing errors » LIST[INTEGER] » LIST[BOOK] » LIST[STRING] 16