meeting -*- outline -*- * Polymorphism They should read Reil's book, ch. 2. ** Different classes can implement the same interface ------------------------------------------ SECOND ITERATION Allow subtraction r1-r2, and multiplication as formulas. ------------------------------------------ *** In Eclipse, rename Formula to Sum, then add Sub, Mult classes. You should *copy* using cut and paste, the Sum class to make the others. We'll see how to use inheritance shortly... package calc; /** Multiplication formulas. * @author Gary T. Leavens */ public class Mult implements FormulaType { /** The left register */ private /*@ spec_public @*/ int left; /** The right register */ private /*@ spec_public @*/ int right; /** * Initialize this Mult object to calculate * the product of the given registers. * @param left the left register's number. * @param right the right register's number */ //@ requires 0 <= left && left < Registers.NUM_REGS; //@ requires 0 <= right && right < Registers.NUM_REGS; //@ ensures this.left == left && this.right == right; public Mult(int left, int right) { this.left = left; this.right=right; } /** * Evaluate the formula and return its value. */ /*@ also @ ensures (* \result is the product of the value of @ the left and right register's values. *); @*/ public double evaluate() { return Registers.get(left) * Registers.get(right); } } Note: Do Diff off-line package calc; /** Difference formulas. * @author Gary T. Leavens */ public class Diff implements FormulaType { /** The left register */ private /*@ spec_public @*/ int left; /** The right register */ private /*@ spec_public @*/ int right; /** * Initialize this Diff object to calculate the * difference of the given registers. * @param left the left register's number. * @param right the right register's number */ //@ requires 0 <= left && left < Registers.NUM_REGS; //@ requires 0 <= right && right < Registers.NUM_REGS; //@ ensures this.left == left && this.right == right; public Diff(int left, int right) { this.left = left; this.right=right; } /** * Evaluate the formula and return its value. */ /*@ also @ ensures (* \result is the difference of the value of @ the left and right register's values. *); @*/ public double evaluate() { return Registers.get(left) - Registers.get(right); } } *** Add test cases for simple products and differences. package tests; import calc.*; import junit.framework.TestCase; /** Tests of the calculator. * @author Gary T. Leavens */ public class TestCalc extends TestCase { /** * Constructor for TestCalc. */ public TestCalc(String name) { super(name); } public static void main(String[] args) { junit.textui.TestRunner.run(TestCalc.class); } /** * @see TestCase#setUp() */ protected void setUp() throws Exception { super.setUp(); } /** * Test simple summations in the calculator. */ public void testSummation1() { FormulaType f1 = new Sum(0, 1); FormulaType f2 = new Sum(1, 2); Registers.put(0, 4.0); Registers.put(1, 8.0); Registers.put(2, -1.0); FormulaMap.put("add01", f1); FormulaMap.put("add12", f2); assertEquals(12.0, FormulaMap.get("add01").evaluate(), 0.01); assertEquals(7.0, FormulaMap.get("add12").evaluate(), 0.01); } /** * Test simple differences in the calculator. */ public void testDiff1() { FormulaType f1 = new Diff(0, 1); FormulaType f2 = new Diff(1, 2); Registers.put(0, 4.0); Registers.put(1, 8.0); Registers.put(2, -1.0); FormulaMap.put("sub01", f1); FormulaMap.put("sub12", f2); assertEquals(-4.0, FormulaMap.get("sub01").evaluate(), 0.01); assertEquals(9.0, FormulaMap.get("sub12").evaluate(), 0.01); } /** * Test simple products in the calculator. */ public void testMult1() { FormulaType f1 = new Mult(0, 1); FormulaType f2 = new Mult(1, 2); Registers.put(0, 4.0); Registers.put(1, 8.0); Registers.put(2, -1.0); FormulaMap.put("prod01", f1); FormulaMap.put("prod12", f2); assertEquals(32.0, FormulaMap.get("prod01").evaluate(), 0.01); assertEquals(-8.0, FormulaMap.get("prod12").evaluate(), 0.01); } } *** Test it, and fix the type errors in the FormulaMap class (so that it works with FormulaTypes, and not Sums). Q: What will be the return type of FormulaMap.get()? FormulaType Q: When we get a Formula from the FormulaMap, how can we evaluate it without knowing it's class? use a method call (dynamic dispatch) package calc; import java.util.HashMap; /** Map from formula names to formulas * @author Gary T. Leavens */ public class FormulaMap { private static HashMap map = new HashMap(); /** * Add the association of the name to the formula, * replacing the old value if any. * @param name the formula's name * @param formula the formula */ //@ requires name != null && formula != null; public static void put(String name, FormulaType formula) { map.put(name, formula); } /** * Retrieve the formula associated with the given name. * @param name the formula's name */ //@ requires name != null; //@ ensures \result != null; public static FormulaType get(String name) { Object o = map.get(name); if (o == null) { throw new IllegalArgumentException( "Unknown formula name: " + name); } else { return (FormulaType) o; } } /** * Is the given name a defined name for a formula? */ public static boolean isDefinedAt(String name) { return map.containsKey(name); } } *** Summary of use of interfaces and polymorphism What we've done is summarized by this class diagram: ------------------------------------------ INTERFACES AND POLYMORPHISM |--------------------| | <> | | FormulaType | |--------------------| |--------------------| | evaluate(): double | |--------------------| / \ | | - - - - - - - - - - - - - - - - - | | | |-------| |-------| |-------| | Sum | | Diff | | Mult | |-------| |-------| |-------| |---------------------------| | FormulaMap | |---------------------------| |---------------------------| | get(String): FormulaType | | put(String, FormulaType) | |---------------------------| We can then write: FormulaType f = FormulaMap.get(name); f.evaluate(); ------------------------------------------ Q: What's the advantage of using 3 separate classes, instead of one class, another instance variable, and a bunch of if-then-else statements? Easier to add new FormulaType classes (to evolve the program) Code is more straightforward (less conditional logic) The system is going to dispatch on the class anyway, so best to take advantage of that, instead of also doing a test on a variable ------------------------------------------ POLYMORPHISM def: Code is polymorphic when it works for objects of more than one class. Example: FormulaType f = FormulaMap.get(name); f.evaluate(); ------------------------------------------ ------------------------------------------ IMPORTANCE OF POLYMORPHISM Helps progammers understand large software systems: they learn interfaces and standard method names they can ignore details Examples: All Java Objects have: boolean equals(Object o); String toString(); int hashCode(); All Iterators have: boolean hasNext(); Object next(); ------------------------------------------ This is the way to understand and learn a big library like the JDK.