240 likes | 440 Views
Polymorphism. Giuseppe Attardi Antonio Cisternino. Generic programming. Code reuse is clearly a good idea Often an algorithm can be applicable to many objects Goal is to avoid rewriting as much as possible Types introduce great benefits but also may limit code reuse:
E N D
Polymorphism Giuseppe Attardi Antonio Cisternino
Generic programming • Code reuse is clearly a good idea • Often an algorithm can be applicable to many objects • Goal is to avoid rewriting as much as possible • Types introduce great benefits but also may limit code reuse: int sqr(int i, int j) { return i*j; } double sqr(double i, double j) {return i*j; } • The notion of sqr is unique but we must define it twice because of types • Languages offer mechanisms to address this problem
Polymorphism • The ability of associate more than one meaning to a name in a program • We have already seen two kinds of polymorphism: • Subtype/inclusion (inheritance) • Overloading • Polymorphism is the fundamental mechanism for generic programming • There are other kinds of polymorphism
Classification of Polymorphism Parametric Universal Inclusion Polymorphism Overloading Ad hoc Coercion
Universal vs. ad hoc polymorphism • With overloading an implementation for each signature is required • We provide ad hoc solutions for different objects • Inheritance instead allows defining algorithms that operate on all classes of objects that inherit from a given class • In this case a single (universal) solution applies to different objects
Implementing Polymorphism • Dynamic method dispatch • C++ adds a v-table to each object from a class having virtual methods
Containers • Example: Java Vector Vector v = new Vector(); v.addElement("Pippo"); v.addElement(new Integer(2)); • Signature of addElement: void addElement(Object x); • The argument is of type Object because the container may contain any type of object
Problem with containers • Inserting an object in a vector we loose type information • In our example we implicitly upcast from String to Object: v.addElement("Pippo"); • Extracting the second element with the wrong cast produces a runtime error: Integer i = (Integer)v.elementAt(0);
Weakest constraint programming • Where do we assume something about objects that we manipulate? class Vector { Object[] v; int size; public Vector() { v = new Object[15]; size = 0; } public addElement(Object e) { if (size == v.length) { Object[] w = new Object[](2 * size); w.copy(v, 0, size); v = w; } v[size++] = e; }} • We assume only assignment operations and arrays: operation available on all objects
Can we sort our vector? • How to add a method for sorting a vector? • We do not have enough information on our objects: no comparison operation is available • Our vector is too generic! • Two solutions: • accept only objects that implement an interface (i.e. IComparable) that exposes a method to compare objects public void addElement(IComparable e) {…} • Pass a functional object: an object which implements an interface for comparing Object instances (i.e. IComparator) public void Sort(IComparator c) {…} interface IComparator { int compare(Object x, Object y); }
Abstract as much as possible! • To express generic code with subtype polymorphism we should abstract the essence of the operations required on the objects we want to manipulate • Risk is over-abstraction: once defined our vector we can’t easily add a sort method • Another issue: inheritance relies on explicit annotation of our types and changes are hard to perform
Iterating over a collection • A common programming pattern is to enumerate the elements of a collection • It doesn’t really matter how the collection is organized • We can implement a class per collection type whose objects enumerates the elements. • Example: Enumeration elements() { return ???; } void printCollection(Enumeration e) { while (e = hasMoreElements()) { Object o = e.nextElement(); System.out.println(o); }} Interface
Question • Which class implements method elements? • Class Vector • Use overloading and singleton class Enumeration { static Enumeration getEnumeration(Vector v){ return v.elements(); } // Other collections’ enumerators } • Thus we can add enumerators to existing collections
Enumerator for Vector class VectorEnum implements Enumeration { int idx; Vector v; bool hasMoreElements() { idx < v.size(); } Object nextElement() { return v.elementAt(idx++); } VectorEnum(Vector v) { idx = 0; this.v = v; // why it is not copied? } }
Is the enumerator up to date? • To ensure that the enumerator is consistent the vector should be copied into the enumerator • This isn’t reasonable: memory wasted and we iterate on a different vector! • There is no way to ensure that the enumerator is consistent with the vector • Nonetheless it is possible to do something: we introduce a “version” of the vector • Each time the vector is modified the version is incremented • The enumerator compares the version of the vector with the one of the vector when it has been created
Event handling in GUI • Before Java 1.1 OO GUI frameworks were based on sub-typing • GUI can be easily described using generic programming: buttons are a subtype of control which is a special window • Containers of graphical widgets operates on controls, irrespective of their types • Event dispatching and handling is dealt by virtual methods • hence by default is delegated to the super-type
Java AWT Event Model class Component { int x, y; bool handleEvent(Event e); } class Button extends Component { String text; bool handleEvent(Event e) { } … } class Window extends Component { … } class Frame extends Window { … }
Event handling class MyButton extends Button { boolean handleEvent(Event e) { switch (e.type) { case Event.MOUSE_UP: … return true; // Event handled! } default: return super.handleEvent(e); }}
Limits of AWT Event Model • Generic programming in this case is quite elegant but inefficient • Propagation of events to a number of handlers, mostly useless • Proliferation of classes: one for each object with different behavior
Alternative • Event Delegation model • Observer Pattern (aka publish/subscribe) • Observable has set of registered observers • Observable notifies its observers when its state changes • Handling performed by objects that provide a Listener interface (aka callback, delegate)
Java JDBC • Java DataBase Connectivity is a specification from Sun for accessing databases in Java • Interesting example of generic programming • It implements a driver architecture exploiting the mechanisms of JVM
Overall architecture • The java.sql package exposes only interfaces • The only class is DriverManager • Using the class constructor a driver register itself with the DriverManager • The programmer performs the following steps: • Load the database driver (a Java class) • Create a connection to a database (using DriverManager) • Obtain a Statement and execute the query • Enumerate the rows using a ResultSet
JDBC example Class.forName("…"); // Load the driver Connection c = DriverManager.getConnection("…"); Statement s = c.createStatement(); ResultSet r = s.executeQuery("select …"); while (r.hasNext()) { // Value of second column as String String s = r.getString(2); }
Question • Statement is Class or Interface? • Connection, Statement are Interfaces • Through the DriverManager the program obtains an object of unknown type which implements the Connection interface • The same applies to all the other interfaces: through the connection an object implementing Statement is obtained, and so on and so forth