<<

Polymorphism

CMSC 330: Organization of Definition Programming Languages • Feature that allows values of different data types to be handled using a uniform

Applicable to • Functions ! Same function applied to different data types Polymorphism ! Example let id = function x -> x • Data types ! Same 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 ! ! Method name overloading • 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. (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 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( 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 Xs as input parameters are OK element type ! If youre allowed to pass Object in, you can pass any X in • Methods that return Xs as results require downcasts

! You only get Objects out, which you need to cast down to X Syntax • Class declaration: class A { ... } ! A is the class name, as before General characteristic of subtype polymorphism ! T is a , can be used in body of class (...) • Client usage declaration: A x; ! We instantiate A with the Integer type

CMSC 330 17 CMSC 330 18

Parametric Polymorphism for Stack Stack Client

class Stack { class Entry { ElementType elt; Entry next; Stack is = new Stack(); Entry(ElementType i, Entry n) { elt = i; next = Integer i; n; } is.push(new Integer(3)); } is.push(new Integer(4)); Entry theStack; i = is.pop(); void push(ElementType i) { theStack = new Entry(i, theStack); No downcasts } ElementType pop() throws EmptyStackException { Type-checked at compile time if (theStack == null) No need to duplicate Stack code for every usage throw new EmptyStackException(); else { • line i = is.pop(); can stay the same even if the type of is isnt an ElementType i = theStack.elt; integer in every path through the program theStack = theStack.next; return i; }}} CMSC 330 19 CMSC 330 20 Parametric Polymorphism for Methods Parametric Polymorphism, Again

String is a subtype of Object But id() doesnt 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 T id(T x) { return x; } Integer i = id(new Integer(3)); Cant pass an Object to 2 or 4 3 doesnt type check • Notice no need to instantiate id; compiler figures out Can pass a String to 1 but you get an Object back the correct type at usage • The formal parameter has type T, the actual parameter has type Integer

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 { ...T x;... } becomes ! class LinkedList { ...} • class A { ...Object x;... } ! class HashMap { ... } Add downcasts wherever necessary ! interface Collection { ... } • Excellent tutorial listed on references page • Integer x = A.get(); becomes • Integer x = (Integer) (A.get());

But they didnt change the JVM to add generics So why did we bother with generics if theyre • 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 becomes class A • Recall type variables T are rewritten to Object

Thus class A is available as a raw type

Disallowed, assuming T is type variable • class A { ... } • new T() would translate to new Object() (error) • class B { A x; } // use A as raw type • new T[n] would translate to new Object[n] (warning)

• 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;

Lets 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

int count(Collection c) { Is Stack a subtype of Stack? int j = 0; • We could have the same problem as with arrays for (Iterator i = c.iterator(); i.hasNext(); ) { • Thus Java forbids this subtyping T e = i.next(); j++; }

Now consider the following method: return j;} int count(Collection c) { int j = 0; for (Iterator i = c.iterator(); i.hasNext(); ) { But requires a dummy type variable that isnt Object e = i.next(); j++; really used for anything } return j; }

! Not allowed to call count(x) where x has type Stack CMSC 330 29 CMSC 330 30

Solution II: Wildcards Legal Wildcard Usage

Use ? as the type variable Reasonable question: • Collection is Collection of unknown • Stack is not a subtype of Stack int count(Collection c) { • Why is Stack a subtype of Collection? int j = 0; for (Iterator i = c.iterator(); i.hasNext(); ) { Object e = i.next(); j++; Answer: } • Wildcards permit reading but not writing return j; }

Why is this safe? • Using ? is a contract that youll 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 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 c) { • this includes Shape itself for (Shape s : c) s.draw(); void drawAll(Collection c) { } for (Shape s : c) • Can we pass this method a Collection? s.draw(); } ! No, not a subtype of Collection This is a bounded wildcard • How about the following? • void drawAll(Collection c) { • We can pass Collection for (Shape s : c) // not allowed, • We can safely treat s as a Shape s.draw(); assumes ? is } Shape

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

T getAndDrawShape(List c) { c.get(1).draw(); return c.get(2); }

• 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