Polymorphism
CMSC 330: Organization of Definition Programming Languages • Feature that allows values of different data types to be handled using a uniform interface
Applicable to • Functions ! Same function applied to different data types Polymorphism ! Example let id = function x -> x • Data types ! Same data type can contain different data types ! Example type a option = None CMSC 330 | Some of a 2
Two Kinds of Polymorphism Polymorphism Overview
Described by Strachey in 1967 Subtype (for OO languages)
Ad hoc and Subtype polymorphism • Sometimes considered ad-hoc • Range of types is finite Ad-hoc • Combinations must be specified in advance • Overloading • Behavior may differ based on type of arguments ! Operator overloading ! Method name overloading Parametric polymorphism • Code written without mention of specific type Parametric ML types • May be transparently used with arbitrary # of types • • Behavior is same for different types of arguments • A.k.a. generic programming (for OO languages) ! Bounded parametric polymorphism combines subtype and parametric polymorphism
CMSC 330 3 CMSC 330 4 Subtype Polymorphism Subtype Polymorphism
Any context expecting an object of type A can be More generally, B is a subtype of A if it can “do” given an object of type B where B is a subtype of A everything A can • Example: Function parameter has type A • Has the “same” fields, methods
• So it may be called with arguments of type B Java interfaces also support subtyping Subtyping enabled by inheritance interface A { … } class B implements A { … } class A { … } class C implements A { … } class B extends A { … } // subclass static void f(A arg) { … } static void f(A arg) { … } B b = new B(); A a = new A(); C c = new C(); B b = new B(); f(b); f(c); // f accepts arg of type B or C f(a); f(b); // f accepts arg of type A or B
CMSC 330 5 CMSC 330 6
When can we extend or implement? Overloading
When can A extend B? Multiple copies of function • Complicated! • Same function name • Roughly: When it overrides methods such that the • But different number / type of parameters overriding types are subtypes of original Arguments determine function actually invoked When can A implement B? • Function is uniquely identified not by function name, • When it has methods whose types are the same as but by name + order & number of argument type(s) those required by the interface ! print(Integer i) → print_Integer(…) ! print(Float f) → print_Float(…) static void print(Integer arg) { … } static void print(Float arg) { … } print(1); // invokes 1st print print(3.14); // invokes 2nd print
CMSC 330 7 CMSC 330 8 Overloading and Overriding Operator Overloading
Interaction is confusing Treat operators as functions • Common mistake is inadvertently overload when you • With special syntax for invocations mean to override • Behavior different depending on operand type public class Point { private int x = 0, y = 0; public boolean equals(Point p) { //overloads Example return (p.x == x) && (p.y == y); } • + in Java public static void main(String args[]) { Point p1 = new Point(); 1 + 2 // integer addition Point p2 = new Point(); 1.0 + 3.14 // float addition Object o = p1; // string concatenation System.out.println(o.equals(p2)); //prints false Hello + world System.out.println(p1.equals(p2)); //prints true } }
CMSC 330 9 CMSC 330 10
Operator Overloading (cont.) Parametric Polymorphism
User-specified operator overloading Found in statically typed functional languages • Supported in languages such as Ruby, C++ • OCaml, ML, Haskell • Makes user data types appear more like native types • Example Examples let hd = function (h::_) -> h a list -> a • Defining function for ^ operator Also used in object oriented programming class MyS class MyS { def ^ (arg) MyS operator^(MyS arg){ • Known as generic programming ...... • Example: Java, C++ end } end } Ruby C++
CMSC 330 11 CMSC 330 12 An Integer Stack Implementation Integer Stack Client class Stack { class Entry { Integer elt; Entry next; Stack is = new Stack(); Entry(Integer i, Entry n) { elt = i; next = n; } Integer i; } is.push(new Integer(3)); Entry theStack; is.push(new Integer(4)); void push(Integer i) { theStack = new Entry(i, theStack); i = is.pop(); } Integer pop() throws EmptyStackException { if (theStack == null) If we also want a stack of Floats, do we throw new EmptyStackException(); need to write a Float Stack class? else { Integer i = theStack.elt; theStack = theStack.next; return i; }}}
CMSC 330 13 CMSC 330 14
An Object Stack Implementation New Stack Client class Stack { Stack is = new Stack(); class Entry { Object elt; Entry next; Integer i; Entry(Object i, Entry n) { elt = i; next = n; } is.push(new Integer(3)); } is.push(new Integer(4)); Entry theStack; void push(Object i) { i = (Integer) is.pop(); theStack = new Entry(i, theStack); Object stacks are polymorphic & reusable } Object pop() throws EmptyStackException { • push() works the same if (theStack == null) throw new EmptyStackException(); • But now pop() returns an Object else { ! Object i = theStack.elt; Have to downcast back to Integer theStack = theStack.next; • Not checked until run-time return i; }}}
CMSC 330 15 CMSC 330 16 General Problem Parametric Polymorphism (for Classes)
When we move from an X container to an Java 1.5 introduced generics
Object container We can parameterize the Stack class by its • Methods that take X s as input parameters are OK element type ! If you re allowed to pass Object in, you can pass any X in • Methods that return X s as results require downcasts
! You only get Objects out, which you need to cast down to X Syntax • Class declaration: class A
CMSC 330 17 CMSC 330 18
Parametric Polymorphism for Stack Stack
class Stack
String is a subtype of Object But id() doesn t care about the type of x 1. static Object id(Object x) { return x; } • It works for any type 2. static Object id(String x) { return x; } 3. static String id(Object x) { return x; } So parameterize the static method 4. static String id(String x) { return x; } static
CMSC 330 21 CMSC 330 22
Standard Library, and Java 1.5 (and later) Translation via Erasure
Part of Java 1.5 (called generics ) Replace uses of type variables with Object • Comes with replacement for java.util.* • class A
But they didn t change the JVM to add generics So why did we bother with generics if they re • How was that done? just going to be removed? • Because the compiler still did type checking for us • We know that those casts will not fail at run time
CMSC 330 23 CMSC 330 24 Limitations of Translation Using with Legacy Code
Some type information not available at compile- Translation via type erasure time • class A
Thus class A is available as a raw type
Disallowed, assuming T is type variable • class A
• Some casts/instanceofs that use T Sometimes useful with legacy code, but... ! Only ones the compiler can figure out are allowed • Dangerous feature to use, plus unsafe • Relies on implementation of generics, not semantics
CMSC 330 25 CMSC 330 26
Subtyping and Arrays Problem with Subtyping Arrays
public class A { ... } Java has one funny subtyping feature public class B extends A { void newMethod(); } • If S is a subtype of T, then ... • S[ ] is a subtype of T[ ] void foo(void) { B[] bs = new B[3]; A[] as;
Let s write methods that take arbitrary arrays as = bs; // Since B[] subtype of A[] public static void reverseArray(Object [] A) { as[0] = new A(); // (1) for(int i=0, j=A.length-1; i Now consider the following method: return j;} int count(Collection ! Not allowed to call count(x) where x has type Stack Solution II: Wildcards Legal Wildcard Usage Use ? as the type variable Reasonable question: • Collection is Collection of unknown • Stack Why is this safe? • Using ? is a contract that you ll never rely on having a particular parameter type • All objects subtype of Object, so assignment to e ok CMSC 330 31 CMSC 330 32 Example: Can Read But Cannot Write c For Loops int count(Collection c) { Java 1.5 has a more convenient syntax for this int j = 0; for (Iterator i = c.iterator(); i.hasNext(); ) { standard for loop Object e = i.next(); int count(Collection c) { c.add(e); // fails: Object is not ? int j = 0; j++; for (Object e : c) } j++; return j; } return j; } • This loop will get the standard iterate and set e to each element of the list, in order CMSC 330 33 CMSC 330 34 More on Generic Classes Bounded Wildcards Suppose we have classes Circle, Square, and We want drawAll to take a Collection of anything Rectangle, all subtypes of Shape that is a subtype of shape void drawAll(Collection CMSC 330 35 CMSC 330 36 Upper Bounded Wild Cards Bounded Wildcards (cont.) ? extends Shape actually gives an upper bound Should the following be allowed? on the type accepted void foo(Collection c) { Shape is the upper bound of the wildcard c.add(new Circle()); } Shape • No, because c might be a Collection of something that is not compatible with Circle Rectangle • This code is forbidden at compile time Circle Square CMSC 330 37 CMSC 330 38 Lower Bounded Wildcards Lower Bounded Wildcards (cont.) Dual of the upper bounded wildcards Now the following is allowed ? super Rectangle denotes a type that is a void foo(Collection c) { supertype of Rectangle c.add(new Circle()); c.add(new Rectangle()); // fails • Type Rectangle is included } ? super Rectangle gives a lower bound on the • Because c is a Collection of something that is always type accepted Shape compatible with Circle Rectangle Circle Square CMSC 330 39 CMSC 330 40 Bounded Type Variables You can also add bounds to regular type vars • This method can take a List of any subclass of Shape ! This addresses some of the reason that we decided to introduce wild cards ! Once again, this only works for methods CMSC 330 41