SOLID Principles

Liskov Substitution Principle

Objects of a superclass should be replaceable with objects of a subclass without affecting the correctness of the program.

12 min readEssential

1What is LSP?

Real-World Analogy
Duck Test

If it looks like a duck, quacks like a duck, but needs batteries - you have the wrong abstraction. A RobotDuck is NOT substitutable for a real Duck if your code expects the duck to eat or fly.

Rectangle-Square Problem

Mathematically, a Square IS-A Rectangle. But in code, if you can set width and height independently on a Rectangle, a Square breaks this - setting width must also change height. Square violates LSP.

Liskov Substitution Principle (LSP): If S is a subtype of T, then objects of type T can be replaced with objects of type S without altering any of the desirable properties of the program. In other words, a child class must be usable anywhere the parent class is expected.

LSP Substitutability Test

✅ LSP Compliant
Bird bird = new Sparrow();
bird.fly(); // Works!

bird = new Eagle();
bird.fly(); // Works!
All birds can be substituted
❌ LSP Violation
Bird bird = new Penguin();
bird.fly(); // Throws error!
// or does nothing
// Penguin cannot substitute Bird
Penguin breaks the contract

2Classic Violation: Rectangle-Square

Rectangle-Square Problem
// ❌ LSP VIOLATION: Square extends Rectangle

class Rectangle {
    protected int width;
    protected int height;

    public void setWidth(int width) {
        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getArea() {
        return width * height;
    }
}

class Square extends Rectangle {
    // Square must maintain width == height
    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width;  // Force height = width
    }

    @Override
    public void setHeight(int height) {
        this.width = height;  // Force width = height
        this.height = height;
    }
}

// Client code that breaks with Square
public void testRectangle(Rectangle rect) {
    rect.setWidth(5);
    rect.setHeight(4);
    // Expects area = 5 * 4 = 20
    assert rect.getArea() == 20;  // FAILS for Square!
    // Square: width=4, height=4, area=16
}

public static void main(String[] args) {
    Rectangle rect = new Rectangle();
    testRectangle(rect);  // ✅ Works: area = 20

    Rectangle square = new Square();
    testRectangle(square);  // ❌ FAILS: area = 16!
    // Square cannot substitute Rectangle
}

3LSP-Compliant Solution

Use proper abstraction - Rectangle and Square are both Shapes, not parent-child:

Proper Abstraction
// ✅ LSP COMPLIANT: Use proper abstraction

interface Shape {
    int getArea();
}

class Rectangle implements Shape {
    private int width;
    private int height;

    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public int getArea() {
        return width * height;
    }
}

class Square implements Shape {
    private int side;

    public Square(int side) {
        this.side = side;
    }

    @Override
    public int getArea() {
        return side * side;
    }
}

// Now client works with any Shape
public void printArea(Shape shape) {
    System.out.println("Area: " + shape.getArea());
}

public static void main(String[] args) {
    List<Shape> shapes = Arrays.asList(
        new Rectangle(5, 4),
        new Square(5)
    );

    for (Shape shape : shapes) {
        printArea(shape);  // ✅ Works for all shapes!
    }
}

// Output:
// Area: 20
// Area: 25

4LSP Rules

1Preconditions cannot be strengthened
Subclass cannot require MORE than parent. If parent accepts null, child must too.
Violation: Parent: accept any positive. Child: accept only even numbers
2Postconditions cannot be weakened
Subclass must guarantee AT LEAST what parent guarantees.
Violation: Parent: returns non-null. Child: might return null
3Invariants must be preserved
Properties that are always true for parent must be true for child.
Violation: Rectangle: width and height are independent. Square: they must be equal
4History constraint
Subclass cannot modify state in ways parent cannot.
Violation: Immutable parent has mutable child

5Signs of LSP Violation

❌ Code Smells
  • instanceof checks in client code
  • Empty or throwing method overrides
  • Subclass ignoring parent behavior
  • Type checking before method call
  • NotImplementedException in overrides
✅ How to Fix
  • Use composition instead of inheritance
  • Create proper abstraction hierarchy
  • Separate interfaces for different behaviors
  • Use IS-A relationship correctly
  • Apply Interface Segregation Principle

6Interview Questions

What is the Liskov Substitution Principle?
LSP states that objects of a parent class should be replaceable with objects of child classes without breaking the program. If S is subtype of T, code using T should work correctly with S. Child must honor parent's contract - same or weaker preconditions, same or stronger postconditions, same invariants.
Give an example of LSP violation.
Classic Rectangle-Square problem: Square inherits from Rectangle. Client sets width=5, height=4, expects area=20. But Square.setHeight() also changes width (to maintain square property), so area=16. Square cannot substitute Rectangle - LSP violation. Solution: Both should implement Shape interface instead.
How do you detect LSP violations?
Signs: 1) instanceof/type checks in client code, 2) Empty override methods that do nothing, 3) Overrides that throw NotImplementedException, 4) Child ignoring parent behavior, 5) Need to check type before calling method. Fix: Use proper abstraction, composition over inheritance, or interface segregation.

7Key Takeaways

1LSP: Subtypes must be substitutable for their base types.
2If child cannot fully honor parent contract, do not use inheritance.
3Classic violation: Rectangle-Square problem.
4Signs: instanceof checks, empty overrides, throwing exceptions.
5Fix: Proper abstraction, composition, interface segregation.
6LSP ensures behavioral compatibility in inheritance.