15-312 Recitation #11: Polymorphism in Java 2002-11-06 Joshua Dunfield (joshuad@cs) Carnegie Mellon University - Non-generic Java: Annoying because of all the downcasts. For example, every time an element of a collection is accessed, it must be downcasted. - Various modifications to the language have been proposed; the most successful has been GJ=Generic Java: Java + polymorphic types All t.tau (written tau) - To maintain compatibility with JVMs, implemented by a translation into ordinary Java.* Generic Java code: interface Comparable { int compareTo (A that); } class Byte implements Comparable { byte value; ... int compareTo (Byte that) { return this.value - that.value; } } class Collections { static > A max (Collection xs) { Iterator xi = xs.iterator(); A w = xi.next(); while (...) { A x = xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } - Translation: interface Comparable { int compareTo (Object that); } class Byte implements Comparable { byte value; ... int compareTo (Byte that) { return this.value - that.value; } int compareTo (Object that) { return this.compareTo((Byte)that); } } class Collections { static Comparable max (Collection xs) { Iterator xi = xs.iterator(); Comparable w = (Comparable) xi.next(); while (...) { Comparable x = (Comparable) xi.next(); if (w.compareTo(x) < 0) w = x; } return w; } } - Subtyping GJ has subtyping as well; a natural question that arises is: how does subtyping work for parameterized (generic, polymorphic) types? As in Assignment 6, the rule for MinML is covariant: {u/t} sigma <= {u/t} tau ------------------------ All s.sigma <= All t.tau In GJ syntax, the question is what rule should conclude C1 <= C2 The answer turns out to differ from MinML, and becomes easier to see if we consider the invariant or "no-variant" rule for ref cells: s <= t t <= s ---------------- s ref <= t ref The intuition for ref cells is that ! behaves like a function whose domain is a ref cell, and ref(-) like a function whose _range_ is a ref cell. So from ! we would think that the rule must be contravariant (by analogy with the rule for ->), and from ref(-) that the rule must be covariant (again, by analogy with the rule for ->). Hence the rule must be *both* contravariant and covariant -- which amounts to requiring that the types s, t be equivalent. Since variables in Java are mutable, everything is essentially a ref cell, and the rule must therefore be C1 <= C2 A1 <= A2 A2 <= A1 ------------------------------ C1 <= C2 * I lied. The translation is actually to JVM, not Java source, because the translation may create methods of the form Integer foo () { ... } Object foo () { ... } in the same class, which Java does not allow (at least, not in '98). However, this is allowed *inside* the JVM (which encodes the types into the names, and therefore makes the two methods have different names). - Further reading (and the source of the above example): Bracha, Odersky, Stoutamire, and Wadler: Making the future safe for the past: Adding genericity to the Java programming language. In Proc. OOPSLA '98. Online at http://www.research.avayalabs.com/user/wadler/gj/Documents/#gj-oopsla