260 likes | 419 Views
Type Abstraction. Liskov, Chapter 7. Liskov Substitution Principle. In any client code, if the supertype object is substituted by a subtype object, the client’s expectations will still be satisfied. Everyone learns this in intro OO courses, but this is a lot harder than it looks.
E N D
Type Abstraction Liskov, Chapter 7
Liskov Substitution Principle In any client code, if the supertype object is substituted by a subtype object, the client’s expectations will still be satisfied. Everyone learns this in intro OO courses, but this is a lot harder than it looks.
Why do we subtype? • Extended Behavior • Standard “Is-A” Relationships • Multiple implementations • SparsePoly, DensePoly • Different implementations • Same specifications • All supertype behavior must be supported • No extra stuff!
Vehicle Car Bike Extended behavior • Extended Behavior • Specialize the behavior of supertype • Classic ‘IS A’ relationship • Usually has additional rep. CAR Vehicle Constraint View: for contracts Object View: for rep
Conflict in two goals? Poly Poly SparsePoly DensePoly LogPoly SparsePoly DensePoly LogPoly: Extends the behavior of Poly by keeping track of how many times it was accessed by the calling code. It has additional rep (a log of accesses) LogPoly
Dispatching Object[] x = new Object[2]; x[0] = new String(“abc”); x[1] = new Integer(1); for(int i=0; i<x.length;i++) System.out.println(x[i].toString()); • Compiler does not complain • Which toString() method is called? Object.toString(), String.toString() or Integer.toString()? • At run time, “best fit” code is called.
MaxIntSet Example (Fig 7.5) public class MaxIntSet extends IntSet { private int biggest; // biggest element of set if not empty public MaxIntSet {super (); } //Why call super() ??? public void insert (int x) { if (size() == 0 || x > biggest) biggest = x; super.insert(x); } public int max () throws EmptyException { if (size() == 0) throw new EmptyException (“MaxIS.max”); return biggest; }
MaxIntSet.remove() public void remove (int x) { super.remove(x); if (size()==0 || x <biggest) return; Iterator g = elements(); biggest = ((Integer) g.next()).intValue(); while (g.hasNext() { int z = ((Integer) g.next()).intValue(); if (z>biggest) biggest = z; } • Need to call supertype’s remove functionality. (private rep!) • Must maintain subtype’s rep invariant
MaxIntSet Abstract State // Overview: MaxIntSet is a subtype of IntSet with an additional // method, max, to determine the maximum element of the set • Two possible abstract states: • {x1, x2, ... xN} - same as IntSet • <biggest, {x1, x2, ... xN}> - visible abstract state • Which one to choose? • Design decision - either is possible • Second may seem more natural, but there are significant advantages to the first. • We will revisit this via Bloch later in the semester.
MaxIntSet.repOk() public boolean repOk() { if (!super.repOk()) return false; if (size() == 0) return true; boolean found = false; Iterator g = elements(); while(g.hasNext()) { int z = ((Integer)g.next()).intValue(); if (z>biggest) return false; if (z==biggest) found = true; return found; }
repOk() and Dynamic Dispatching public class IntSet { public void insert(int x) {...; repOk();} public void remove(int x) {...; repOk();} // where to? public boolean repOk() {...} } public class MaxIntSet extends IntSet { public void insert(int x) {...; super.insert(x); repOk();} public void remove(int x) {super.remove(x); ...; repOk();} public boolean repOk() {super.repOk(); ...;} } MaxIntSet s = {3, 5}; s.remove(5); // repOk()????
Meaning of subtypes • Subtypes behavior must support supertype behavior – (SP) • In particular following three properties: • Signature Rule • Methods Rule • Properties Rule
Signature Rule • Subtypes must have all methods of supertype • Signatures of methods must be compatible with supertype signature • Return types identical; Covariant after Java 1.5 • Guaranteed by Java compiler • Caution: Overriding vs. overloading public boolean equals(Foo foo) {...} public boolean equals(Object foo) {...} • Exceptions • Signature Rule allows Subtype to throw fewer • But methods rule must be satisfied
Methods Rule • When object belongs to subtype, subtype method is called • We must still be able to reason about these methods using supertype specs Suppose SortedIntSet extends IntSet IntSet x = new IntSet(); IntSet y = new SortedIntSet(); x.insert(3); //What is this_post? y.insert(3); //What is this_post?
Methods Rule • Cannot take away methods! • Subtype API should atleast be equal or greater than supertype API • Must maintain the contract! • Precondition rule: What can a subclass do with preconditions in supertype spec? • Post condition rule: What can a subclass do with postconditions in supertype spec?
Precondition rule • Subtype is allowed to weaken the precondition! • Formally: • pre_super |- pre_sub • Super //Requires: x > 5 • Case 1: Sub //Requires x > 6 • Case 2: Sub // Requires x > 4 • x>5 x>4? Which is weaker? • x>5 x>6? • Not checked by compiler
Post condition rule • Informally, subtype is allowed to strengthen the post condition • Formally: • pre_super && post_sub |- post_super • Super: // Effects: returns y < 5 • Sub: //Effects: returns y < 4 • Sub: //Effects: returns y < 6 • Which one is a stronger condition?
Same Diagram as Method Verification Supertype State (Pre-Super) Supertype State (Post-Super) SuperType Method Contract ? AF() AF() Subtype State (Post-Sub) Subtype State (Pre-Sub) Subtype Method Contract
Super public void addZero() //pre: this is not empty //post: add zero to this public void addZero() throws ISE //pre: this is not empty //post: add zero to this Sub public void addZero() //post: add zero to this public void addZero() throws ISE //post: if this is empty, throw ISE else add zero to this Examples Satisfies Signature and Method rules Satisfies Signature and Method rules
Super public void addZero() //pre: this is not empty //post: add zero to this public void addZero() throws ISE //post: if this is empty, throws ISE // else add zero to this Sub public void addZero() throws ISE //post: add zero to this public void addZero() //post: add zero to this More examples Does not satisfy Signature rule Does not satisfy Postcondition part of methods rule
A Java Example • What may subtypes of Iterator do?
Client code private void foo { … try{ o.addZero(); } (catch ISE){ //do something: Client expects to get here! } }
Methods rule vs. Properties rule • Methods rule is for single method invocation • Properties rule about abstract objects. • Invariants: E.g. IntSets do not contain duplicates • s.isIn(x) following s.remove(x) always false • Evolution properties: E.g. MonotoneSets only grow (no remove method allowed).
Liskov 7.8, 7.9, 7.10 public class Counter{ // Liskov 7.8 public Counter() //EFF: Makes this contain 0 public int get() //EFF: Returns the value of this public void incr() //MOD: this //EFF: Increments value of this } public class Counter2 extends Counter { // Liskov 7.9 public Counter2() //EFF: Makes this contain 0 public void incr() // MOD: this //EFF: double this } public class Counter3 extends Counter { // Liskov 7.10 public Counter3(int n) //EFF: Makes this contain n public void incr(int n) // MOD: this //EFF: if n>0 add n to this }
Anaylsis • Signature rule: Careful with over- load vs. ride • Counter2 ok? • Counter3 ok? • Methods rule: • Precondition rule: • Counter 2 ok? • Counter 3 ok? • Postcondition rule: • Counter 2 ok? • Counter 3 ok?
More About Properties Rule Collection <String> c = ...; c.add (“cat”); c.add (“cat”); c.remove(“cat”); // consider the following observer call: // What is behavior if c is a Set? // What is behavior if c is a Bag? if (c.contains(“cat”) { ... } // Such “algebraic” relations are extremely useful for testing