Polymorphism Discrete Mathematics and Its Applications Baojian Hua
Variables and Types Languages such as C or Java have a relative strict semantics to variables use Each variable has a static (declared) type Variables declared before use
Examples // Examples from C: int i; i = 99; double f; f = 3.14; // compiler complains i = f; // Examples from Java: class A{} class B{} A a; a = new A (); // compiler complains a = new B();
What ’ s Polymorphism? A polymorphic typed variable could hold any type of values poly: various morphism: shapes How to declare and use such kind of variables?
What We Want? // Suppose a variable x is polymorphic, we want // to write: x = 99; x = 3.14; x = “hello”; // But how to declare such a variable in // statically typed language as C? // What the “type” of x should be? type x;
Difficulties In C or Java, compiler automatically allocates space for every declared variable And the size of that space is calculated statically (at compile-time).
Difficulties // Examples: int i; // 4 bytes double f; // 8 bytes struct pt2d { int x; double y; } s; // 12 types // So it seems that we can never declare such // kind of a variable in C …
The Magic The magic is that: if we want to make a variable x hold any type (size) of data, then the only way is not to put this data in that variable x.
The Magic // Hummm, thus x must be a pointer (x holds some // data d, but the data d is not in x---an // indirection). // Try #1: int *x; x = malloc (sizeof(int)); *x = 88; // but x could only points to data of size 4 // How to make x point to other sized data? x 88
The Magic // Try #2: make x point to float data: int *x; x = malloc (8); *x = 3.14; // Try this demo … // What happened here? *x = 3.14; x
The Magic // Try #3: let’s cheat the compiler: int *x; x = malloc (8); *((double *)x) = 3.14; // Try this demo … x
The Magic // Try #4: a structure: struct s { int i; double f; }; int *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3.14; // Try this demo … x
Moral So, every pointer is essentially a polymorphic value could point to any type (size) of value the trick is ugly type conversion Every time we want a polymorphic variable, we declare an arbitrary pointer type But the “ int * ” is a little misleading C ’ s early convention (char *) now it offers “ void * ” compiler emits more meaningful error message
Void * // The use of “void *” struct s { int i; double f; }; void *x; x = malloc (sizeof (struct s)); ((struct s *)x)->i = 3; ((struct s *)x)->f = 3.14; // Try this demo … x
Polymorphic Data Structures Structure: relationships linear, tree, graph, hash, … Data structures: relationships between data not the data themselves Polymorphic data structures data are polymorphic Next, I ’ ll take linear list as a running example
Linear List (Linked-based) // a list of integers: typedef struct linkedList *linkedList; struct linkedList { int data; linkedList next; }; void insertHead (linkedList l, int data); int lookup (linkedList l, int data);
Linear List (Linked-based) // implementation of “insertHead” void insertHead (linkedList l, int data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) // implementation of “lookup” int lookup (linkedList l, int data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // note equality! return 1; temp = temp->next; } return 0; }
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); }
Linear List (Linked-based) // another linked list of doubles: typedef struct linkedList *linkedList; struct linkedList { double data; linkedList next; }; void insertHead (linkedList l, double data); int lookup (linkedList l, double data);
Linear List (Linked-based) // implementation of “insertHead” void insertHead (linkedList l, double data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; } // See? Code duplicated!
Linear List (Linked-based) // implementation of “lookup” int lookup (linkedList l, double data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // note equality! return 1; temp = temp->next; } return 0; } // See? Code duplicated!
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, 3.14); }
Linear List (Linked-based) // a polymorphic linked list: typedef struct linkedList *linkedList; struct linkedList { void *data; linkedList next; }; void insertHead (linkedList l, void *data); int lookup (linkedList l, void *data);
Linear List (Linked-based) // implementation of “insertHead” void insertHead (linkedList l, void *data) { linkedList temp = malloc (sizeof (*temp)); temp->data = data; temp->next = l->next; l->next = temp; return; }
Linear List (Linked-based) // implementation of “lookup” int lookup (linkedList l, void *data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // Right??? return 1; temp = temp->next; } return 0; }
Client Code #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, ???); } We should turn data d into a pointer p, and link p here!
Client Code // a list of “integers” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insertHead (list, p); }
Client Code // a list of “doubles” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (8); *((double *)p) = 3.14; insertHead (list, p); } // The burden is lifted to user of linkedList!
Pros. and Cons. of Polymorphism Polymorphism pros: code reuse: write once, use in arbitrary contexts ADT: data structures won ’ t change client data (won ’ t know) Polymorphism cons: Inconsistency (safety issues) Complexity Efficiency We ’ d discuss cons. issues next
Problem #1: Inconsistency (Safety Issues) #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++){ p = malloc (4); *((int *)p) = i; insertHead (list, p); } double *f = listGetHead (list); // ever worse: int (*p)() = listGetHead (list); (*p) ();
Cure to Problem #1: Inconsistency (Safety Issues) C has no built-in static or dynamic checking against such inconsistency Runtime error segment fault, core dumped In C. It ’ s programmers ’ duty to guarantee this! Important: always keep your data structure invariants in mind!
Problem #2: Complexity // implementation of “lookup” int lookup (linkedList l, void *data) { linkedList temp = l->next; while (temp) { if (temp->data == data) // Right??? return 1; temp = temp->next; } return 0; }
Problem #2: Complexity // Recall the definition of polymorphic variables: void *p, *q; // We want to write a function “equals ()” int equals (void *x, void *y); // How to implement this? xy
Problem #2: Complexity // Try #1: int equals (void *x, void *y) { return (x==y); // right? } xy
Problem #2: Complexity // Try #2: int equals (void *x, void *y) { return (*x==*y); // right? } xy
Cure to Problem #2: Extra Comparing Function // Try #2: typedef int (*tyEq) (void *, void *); int equals (void *x, void *y, tyEq eq) { return (eq (x, y)); } xy
Client Code int comp (void *p, void *q){ return (*p==*q); } //////////////////////////////////////// void *x = malloc (4); *x = 9; void *y = malloc (4); *y = 9; equals (x, y, comp);
Client Code int comp (void *p, void *q){ return (p->i==q->i && p->f==q->f); } //////////////////////////////////////// void *x = malloc (sizeof (struct s)); *x = …; void *y = malloc (sizeof (struct s)); *y = …; equals (x, y, comp); // A mimic of so-called “callback”.
Cure to Problem #2: Function Pointers in Data int equals (void *x, void *y) { return (x->eq (x, y)); } xy eq Essential features of OO programming!
Problem #3: Efficiency // a list of integers #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); } // a list of “integers” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insertHead (list, p); }
Boxed Data Polymorphism does not come free data must be heap-allocated, to cope with the “ void * ” pointer convention makes memory management harder It ’ s programmers ’ duty to recycle garbage Such kind of data are called “ boxed ” and “ void * ” is essentially a mask popularize the technology of garbage collection
A Glimpse on Other Forms of Polymorphism // In C void *p; p = malloc (4); *((int *)p) = 99; … printf (“%d\n”, *((int *)p)); equals (p, q, eq); // In Java Object p; p = new Integer (99); … System.out.println (((Integer)p).intValue ()); p.equals (q);
Linked List in Java class LinkedList { Object data; LinkedList next; void insert (Object data) {…} Object getFirst () {…} }
Client Code import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // Or: for (int i=0; i<10; i++) list.insert (new String (“hello”));
Problem #1: Inconsistency (Safety Issues) import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error String s = list.getFirst ();
Problem #1: Inconsistency (Safety Issues) import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // run-time exception String s = (String)list.getFirst ();
Cure to Problem #1: Generic class LinkedList { X data; LinkedList next; void insert (X data) {…} X getFirst () {…} }
Cure to Problem #1: Use of Generic import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error list.insert (new String (“hello”));
Cure to Problem #1: Use of Generic import util.LinkedList; LinkedList list = new LinkedList (); for (int i=0; i<10; i++) list.insert (new Integer (i)); // compile-time error list.insert (new String (“hello”)); // compile-time error String s = list.getFirst ();
Problem #2: Complexity // Turn back to “equals ()” function int equals (Object y); // How to implement this? xy
Cure to Problem #2: Dynamic Method Dispatch Every class has an “ equals ” method, the call to “ equals ” is automatically dispatched to the correct ones in the current called objects. xy eq
Problem #3: Efficiency // a list of integers #include “linkedList.h” … linkedList list = newLinkedList (); for (int i=0; i<10; i++) { insertHead (list, i); } // a list of “integers” #include “linkedList.h” … linkedList list = newLinkedList (); void *p; for (int i=0; i<10; i++) { p = malloc (4); *((int *)p) = i; insertHead (list, p); }
Problem #3: Efficiency Nearly all data in Java are boxed typically heap-allocated in all compilers space automatically allocated by the compilers transparent to the programmers Rely on garbage collection to recycle dead objects