Polymorphism
Download
Report
Transcript Polymorphism
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.
Interface
Example:
Enumeration elements() { return ???; }
void printCollection(Enumeration e) {
while (e = hasMoreElements()) {
Object o = e.nextElement();
System.out.println(o);
}}
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
Possible solution: introduce a “version” of
the vector
Each time the vector is modified the version
is incremented
Enumerator compares the version of the
vector with the one at time of creation
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