The Substitution Principle SWE 332 – Fall 2010 Liskov Substitution Principle In any client code, if subtype object is substituted for supertype object, the client’s expectations are still met Simple example: Object o = getNewObject(); Case 1: Case 2: // client call public Object getNewObject() {...} public String getNewObject() {...} 2 Dynamic 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? Apparent type is fine! Object.toString() String.toString() Integer.toString() At run time, “best fit” code is called. 3 Two valid reasons to subtype Multiple implementations Ideally, client unaware of multiple implementations Example: Extended Behavior Set interface HashSet, TreeSet implementations Usual reason for subtyping Note what is not a reason: Making implementer’s life easier 4 Extended behavior Extended Behavior Specialize the behavior of supertype Classic ‘IS A’ relationship Additional state (abstract or representation) Warning: Harder than it looks! Bike Car Vehicle Constraint View: for contracts CAR Vehicle Object View: for rep 5 Analyzing subtype METHODS Subtypes behavior must support supertype behavior Liskov Substitution Principle Three rules for subtype methods: 1. 2. 3. Signature Rule Methods Rule Properties Rule 6 Signature Rule Guaranteed by Compiler Subtypes must have all methods of supertype Signatures of methods must be compatible with supertype signature Sounds obvious, but programmers often try to get around this Typically, return types are the same Covariance: Subclass may return a subtype Exceptions: Signature Rule allows fewer exceptions But Methods Rule may be in conflict Methods Rule always has priority 7 More concretely… public class (alt. interface) Super{ /** * m() is defined … (pre) * m() does the following things (post) * @throws … (post) */ public T1 m (T2: t2, T3: t3) throws E1, E2… } public class Sub extend (alt. implements) Super{ /** ??? */ public T1 m(T2: t2, T3: t3) throws E1, E2… } 8 Methods Rule When object belongs to subtype, subtype method is called Supertype specifications are necessarily inherited For analysis, consult pre/post See prior slide 9 Methods Rule Must maintain the contract! 1. 2. 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? 10 Precondition rule Subtype is allowed to weaken the precondition Formally: If pre_super, then pre_sub Super //pre x > 5 Case 1: Sub //pre x > 6 Case 2: Sub // pre x > 4 x>5 x>4? Which is weaker? x>5 x>6? Not checked by compiler 11 Post condition rule Subtype is allowed to strengthen the post condition in a consistent way Formally: If pre_super, and sub_post, then super_post Super: // post: returns y < 5 Sub: //post: returns y < 4 Sub: //post: returns y < 6 Which one is a stronger condition? 12 Same Diagram as Method Verification Supertype State (Pre-Super) Supertype State (Post-Super) SuperType Method Contract ? AF() AF() Subtype State (Pre-Sub) Subtype Subtype State (Post-Sub) Method Contract 13 Examples Super Satisfies Signature and Method rules Sub public void addZero() //pre: this is not empty //post: add zero to this public void addZero() //post: add zero to this public void addZero() throws ISE //pre: this is not empty //post: add zero to this public void addZero() throws ISE //post: if this is empty, throw ISE else add zero to this Satisfies Signature and Method rules 14 More examples Super Does not satisfy Signature rule 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 Does not satisfy Postcondition part of methods rule 15 Client code: Liskov Substitution Principle private void foo { … try{ o.addZero(); } catch (ISE e){ //do something: Client expects to get here! } } 16 Methods rule vs. Properties rule Methods rule is for single method invocation Properties rule about general object behavior Invariants: Example: Sets do not contain duplicates Evolution properties: Example: Monotone sets only grow No remove() method allowed Properties must be explicit (i.e. in the JavaDoc) 17 More About Properties Rule Collection <String> c = ...; c.add (“cat”); c.add (“cat”); c.remove(“cat”); // // // if consider the following observer call: What is behavior if c is a Set? What is behavior if c is a Bag? (c.contains(“cat”) { ... } // Such “algebraic” relations are extremely useful for testing 18