CPSC 210
D2: Liskov Substitution Principle (LSP)

Slides designed by Felix Grund, based on Paul Carter’s slides
Learning Goals
- To state the Liskov Substitution Principle (LSP)
- To identify violations of the LSP in existing code
- To ensure the LSP is satisfied when designing type hierarchies

LSP
Nutshell Explanation
LSP - Nutshell Explanation
-
When a subtype is substituted for its super type:
-
the subtype must provide the expected behaviours of the super type
- any test based on the super type must pass on the subtype
-
the subtype must provide the expected behaviours of the super type
@BeforeEach
public void setup() {
this.bird = new Eagle();
}
@Test
public void testBirdFlies() {
// THIS TEST SHOULD PASS
// REGARDLESS OF THE ACTUAL TYPE
assertTrue(this.bird.fly());
}Bird.fly()Eagle.fly()bird.fly()eagle.fly()LSP
Emotional Explanation
LSP - Emotional Explanation



-
Subtype method must NOT:
- narrow the range of inputs that it accepts (specified in requires clause / precondition)
- widen the range of outputs that it produces (specified in effects clause / postcondition)
no change
no change
wider
narrower
narrower
wider
Preconditions
Postconditions
LSP
Scientific Explanation
LSP - Scientific Explanation
- Principle in object-oriented programming, stating:
- if S is a subtype of T,
- then objects of type T may be substituted with objects of type S,
- without altering any of the desirable properties of the program
- Strong behavioral subtyping
- Semantic rather than syntactic relation
LSP - Scientific Explanation (2)


Barbara Liskov
LSP - Example (1)
Superclass
Subclass
// REQUIRES: input >= 0
// EFFECTS: sets the input
public void setInput(int input) {
this.input = input;
}// REQUIRES: input >= 100
// EFFECTS: sets the input
@Override
public void setInput(int input) {
super.setInput(input);
}Y
N
Y
Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C


LSP - Example (2)
Superclass
Subclass
// REQUIRES: input >= 0
// EFFECTS: sets the input
public void setInput(int input) {
this.input = input;
}// REQUIRES: input >= -100
// EFFECTS: sets the input
@Override
public void setInput(int input) {
super.setInput(input);
}N
N
N
Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C


LSP - Example (3)
Superclass
Subclass
// EFFECTS: returns an integer
// in the range [0, 100)
public int produceValue() {
return input % 100;
}// EFFECTS: returns an integer
// in the range [0, 200)
@Override
public int produceValue() {
return input % 200;
}Y
N
Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C
Y


LSP - Example (4)
Superclass
Subclass
// EFFECTS: returns an integer
// in the range [0, 100)
public int produceValue() {
return input % 100;
}// EFFECTS: returns an integer
// in the range [0, 50)
@Override
public int produceValue() {
return input % 50;
}N
N
Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C
N


LSP
Another Example
LSP - Another Example (1)
public class Square extends Rectangle {
@Override
public int setDimensionsReturnSurfaceArea(int height, int width) {
if (height != width) {
throw new RuntimeException("For a Square implementation height must match width");
}
return super.setDimensionsReturnSurfaceArea(height, width);
}
}
public class Rectangle {
protected int height;
protected int width;
public int setDimensionsReturnSurfaceArea(int height, int width) {
this.height = height;
this.width = width;
return height * width;
}
}
Valid inputs become more limited with subtype!
LSP - Another Example (2)
public class ShapeTest {
private Rectangle rectangle;
@BeforeEach
public void setup() {
this.rectangle = new Square();
}
@Test
public void testSetDimensionsReturnSurfaceArea() {
// THIS WILL THROW AN EXCEPTION!
int surfaceArea = this.rectangle.setDimensionsReturnSurfaceArea(10, 20);
assertEquals(200, surfaceArea);
}
}
Test based on the super type does not pass on the subtype!
Pod Activity (Do in Groups of 3-5)

- Why do we need the Liskov Substitution Principle?
- Why is the LSP in the "design" section of CPSC 210 rather than somewhere (Construction for e.g.) ?
- What does the LSP help us achieve in our code?
The Real World: full of violations
List<String> list = Arrays.asList("One", "Two");
list.remove("One");


https://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList-T...-

https://stackoverflow.com/questions/1624144/unsupportedoperationexception-when-trying-to-remove-from-the-list-returned-by-ar
Lecture Ticket
Lecture Ticket (1)
Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C
Superclass
Subclass
// REQUIRES: a sorted tree
// EFFECTS: returns true if the element is
// in the tree, returns false otherwise// REQUIRES: any tree
// EFFECTS: returns true if the element is
// in the tree, returns false otherwiseN
N
N
Lecture Ticket (2)
Superclass
Subclass
// REQUIRES: any tree
// EFFECTS: returns true if the element is
// in the tree, returns false otherwise// REQUIRES: a sorted tree
// EFFECTS: returns true if the element is
// in the tree, returns false otherwiseIs precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C
Y
N
Y
Lecture Ticket (3)
Superclass
Subclass
// REQUIRES: a sorted tree
// EFFECTS: returns 1 if the element is
// in the tree, returns 0 otherwise// REQUIRES: a sorted tree
// EFFECTS: if the element is in the
// tree, returns the number of elements
// in the tree that are larger,
// otherwise returns 0Is precondition narrower?
Is postcondition wider?
Is LSP violated?
A
B
C
N
Y
Y
⛽
Lecture Lab

D2: Liskov Substitution Principle
The End - Thank You!
D2: Liskov Substitution Principle
By firas_moosvi
D2: Liskov Substitution Principle
- 261