// CSE 341 Sp11 Lecture 25. Extensibility in Java // simple expressions abstract class Exp { abstract public int eval(); abstract public String makeString(); // could override toString abstract public boolean hasZero(); } class Int extends Exp { int i; Int(int _i) { i = _i; } public int eval() { return i; } public String makeString() { return Integer.toString(i); } public boolean hasZero() { return i==0; } } class Negate extends Exp { Exp e; Negate(Exp _e) { e = _e; } public int eval() { return - e.eval(); } public String makeString() { return "-(" + e.makeString() + ")"; } public boolean hasZero() { return e.hasZero(); } } class Add extends Exp { Exp e1; Exp e2; Add(Exp _e1, Exp _e2) { e1 = _e1; e2 = _e2; } public int eval() { return e1.eval() + e2.eval();} public String makeString() { return "(" + e1.makeString() + " + " + e2.makeString() + ")"; } public boolean hasZero() { return e1.hasZero() || e2.hasZero(); } } class Mult extends Exp { Exp e1; Exp e2; Mult(Exp _e1, Exp _e2) { e1 = _e1; e2 = _e2; } public int eval() { return e1.eval() * e2.eval();} public String makeString() { return "(" + e1.makeString() + " * " + e2.makeString() + ")"; } public boolean hasZero() { return e1.hasZero() || e2.hasZero(); } } // now we want to add a variant -- easy class Power extends Exp { Exp e1; Exp e2; private int pow(int x, int y) { //assumes y>=0 return y==0 ? 1 : x*pow(x,y-1); } Power(Exp _e1, Exp _e2) { e1 = _e1; e2 = _e2; } public int eval() { return pow(e1.eval(), e2.eval()); } public String makeString() { return "(" + e1.makeString() + " ^ " + e2.makeString() + ")"; } public boolean hasZero() { return e1.hasZero() || e2.hasZero(); } } // but adding an operation is not so easy... interface ExpBad { int eval(); String makeString(); boolean hasZero(); int sumInts(); } class NegateBad extends Negate implements ExpBad { // must have constructor take an Exp, but want to require ExpBad! NegateBad(Exp e) { super(e); } // downcast is a potential run-time failure! public int sumInts() { return ((ExpBad) this.e).sumInts(); } } // ... // If we can modify existing code, we can add a new abstract method to Exp // and a new concrete method in each subclass. // * Exhaustiveness checking on abstract methods means the type-checker lists // what needs doing. // * A great reason not to implement methods that throw "cannot handle" // exceptions. // There is a clever use of dynamic dispatch to allow operation // extensibility. It is called the "visitor pattern" or "double // dispatch". It is worth learning.