CSC152 2006S, Class 25: Parameterized Types (Generics) Admin * Exams due! * Sorry for any frustration. * I expect to return them next Tuesday. * Silly gifts from the trip. * Read "Arrays and Vectors" (available this afternoon). Overview: * A problem * One solution and its problems * A better solution and its problems * How Java generics provide an even better solution * Generics and comparison The problem: Swapping x and y ... Integer x; Integer y; ... // Set x to the larger of x and y // Set y to the smaller of x and y // If x and y are the same, do nothing if (x.compareTo(y) >= 0) { // Do nothing, they're already correct } else { // Swap the two values Integer dummy; dummy = x; x = y; y = dummy; } * When writing the same code over and over again, write a method public void swap(Integer x, Integer y) { Integer dummy; dummy = x; x = y; y = dummy; } Does it work? Integer a = new Integer(5); Integer b = new Integer(100); swap(a,b); pen.println("a = " + a); pen.println("b = " + b); What is the output? What do we want? What do we get? * Possibility one: What we get a = 5 b = 100 * Possibility two: What we want a = 100 b = 5 Improving swap: Assume we have Integer.setValue(int newvalue) and int Integer.getValue() public void swap(Integer x, Integer y) { Integer dummy = new Integer(0); dummy.setValue(x.getValue()); x.setValue(y.getValue()); y.setValue(dummy.getValue()); } This would work. Unfortunately, makes a *bad* assumption * setValue and getValue don't exist What do we do? * Write code for them public class IntegerVariable { Integer value; public IntegerVariable(Integer _value) { this.value = _value; } public Integer get() { return this.value; } public void set(Integer _newvalue) { this.value = _newvalue; } public void swap(IntegerVariable withme) { Integer tmp = this.get(); this.set(withme.get()); withme.set(tmp); } public String toString() { return value.toString(); } } // class IntegerVariable IntegerVariable a = new IntegerVariable(new Integer(5)); IntegerVariable b = new IntegerVariable(new Integer(100)); a.swap(b); pen.println("a = " + a); pen.println("b = " + b); It works correctly! But ... it only works for integers. We can create a StringVariable and a DoubleVariable and ... How do we create all of those: * Write the code again and again * Write a program to write the same code again and again pen.println("public class " + className + "Variable"); pen.println("{"); pen.println(" " + className + " value;"); ... * Write a more generic Variable class: Use polymorphism - Write a more generic thing that accepts different classes that all meet the same interface or inherit from the same superclass public class Variable { Object value; public Variable(Object _value) { this.value = _value; } public Object get() { return this.value; } public void set(Object _newvalue) { this.value = _newvalue; } public void swap(Variable withme) { Object tmp = this.get(); this.set(withme.get()); withme.set(tmp); } public String toString() { return value.toString(); } } // class Variable Variable a = new Variable(new Integer(5)); Variable b = new Variable(new Integer(100)); a.swap(b); pen.println("a = " + a); pen.println("b = " + b); Suppose we want to double the value stored in a a.set(a.get().multiply(Integer.TWO)); What happens? Java doesn't like it - a.get() returns an Object (even though we know it returns an integer) ; Won't compile We tell Java it's an integer a.set(((Integer) a.get()).multiply(Integer.TWO)); * Uglier code, but it works ... a.swap(c); a.set(((Integer) a.get()).multiply(Integer.TWO)); Whoops ... we now have to manually do some of the checks that Java used to do automatically. (E.g., that a and c contain the same type of value or that a contains an integer after the swap) The (new) solution: Generics (parameterized classes) * A technique for building a (possibly infinite) set of similar classes by parameterizing the definition public class ClassName { T value; ... } When constructing, use an actual type Variable a = new Variable(new Integer(5)); Variable b = new Variable(new Integer(100)); a.swap(b); pen.println("a = " + a); pen.println("b = " + b); a.set(a.get().multiply(Integer.TWO)); // OKAY Yay! Java does the type checking for us Variable c = ...; a.swap(c); // Java complains! public class Variable { T value; public Variable(T _value) { this.value = _value; } public T get() { return this.value; } public void set(T _newvalue) { this.value = _newvalue; } public void swap(Variable withme) { T tmp = this.get(); this.set(withme.get()); withme.set(tmp); } public String toString() { return value.toString(); } } // class Variable We've already used generics: public class Foo implements Comparable { public boolean compareTo(Foo other) ... Another common use of generics: java.util.Comparator Comparators provide a compare(T alpha, T beta) method that returns < 0 if alpha precedes beta according to this comparison metric 0 if naturally equal > 0 if alpha follows beta according to this comparison metric public class ComparePeopleByHeight implements Comparator { public int compare(Person alpha, Person beta) { return alpha.height - beta.height; } } Comparator byHeight = new ComparePeopleByHeight(); if (byHeight.compare(eron,nakken) > 0) { pen.println("In your dreams Aaron."); } else { pen.println("the natural order has been preserved."); }