# Liskov Substitution Principle (LSP) in Java

# Introduction

When designing software using **inheritance**, a common mistake developers make is creating subclasses that **change expected behavior**.

This leads to:

Unexpected bugs  
Broken polymorphism  
Difficult maintenance

The **Liskov Substitution Principle (LSP)** helps prevent this.

It is the **third principle** in the **SOLID design principles**, introduced by **Barbara Liskov**.

* * *

# Definition of LSP

**Liskov Substitution Principle states:**

> **Objects of a superclass should be replaceable with objects of its subclass without affecting program correctness.**

* * *

# Simple Understanding

If:

```java
Parent obj = new Child();
```

Then:

👉 The program should **still behave correctly**

👉 The subclass **must not break expected behavior**

That is the **core idea of LSP**.

* * *

# 🧩 Why LSP Matters in Java

Java heavily uses:

*   Inheritance
    
*   Polymorphism
    
*   Method overriding
    

If subclasses behave differently than expected, it leads to:

🚩 Runtime errors  
🚩 Logical bugs  
🚩 Hard-to-debug systems

LSP ensures:

✔ Safe inheritance  
✔ Reliable polymorphism  
✔ Maintainable code

* * *

# Example — Violating LSP (Rectangle–Square Problem)

This is the **most famous example** of LSP violation.

## Step 1 — Create Rectangle Class

```java
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;
    }
}
```

* * *

## Step 2 — Create Square Class (Wrong Design)

```java
class Square extends Rectangle {

    @Override
    public void setWidth(int width) {
        this.width = width;
        this.height = width; // forces square behavior
    }

    @Override
    public void setHeight(int height) {
        this.width = height;
        this.height = height; // forces square behavior
    }
}
```

* * *

## Step 3 — Test Code

```java
public class Main {

    public static void main(String[] args) {

        Rectangle rectangle = new Square();

        rectangle.setWidth(5);
        rectangle.setHeight(10);

        System.out.println(rectangle.getArea());
    }
}
```

* * *

# Expected vs Actual Output

### Expected:

```java
50
```

### Actual:

```java
100
```

* * *

# 🚨 Why This Violates LSP

Because:

*   Rectangle expects width and height to be **independent**
    
*   Square forces width and height to be **equal**
    

So:

👉 Square **changes behavior**

👉 Rectangle cannot be safely replaced

👉 **LSP is violated**

* * *

# 🧩 Problem Flow (Violation Case)

```plaintext
Rectangle expected behavior
        ↓
Square modifies behavior
        ↓
Rectangle replaced by Square
        ↓
Unexpected result occurs
        ↓
Program correctness breaks 
```

* * *

# Correct Design — Following LSP

Instead of forcing inheritance, we use **abstraction**.

* * *

## Step 1 — Create Abstract Shape

```java
abstract class Shape {

    abstract int getArea();
}
```

* * *

## Step 2 — Create Rectangle Class

```java
class Rectangle extends Shape {

    private int width;
    private int height;

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

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

* * *

## Step 3 — Create Square Class

```java
class Square extends Shape {

    private int side;

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

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

* * *

## Step 4 — Test Code

```java
public class Main {

    public static void main(String[] args) {

        Shape rectangle = new Rectangle(5, 10);
        Shape square = new Square(5);

        System.out.println(rectangle.getArea());
        System.out.println(square.getArea());
    }
}
```

* * *

# Output

```java
50
25
```

Now:

✔ No behavior change

✔ Safe substitution

✔ LSP followed

* * *

# 🧠 LSP Design Flow (Correct Case)

```plaintext
Create Parent Abstraction
            ↓
Create Independent Child Classes
            ↓
Override Behavior Safely
            ↓
Replace Parent with Child
            ↓
Program works correctly ✔
```

* * *

# 🧩 Real-World Example — Bird System

Let's look at a realistic case.

* * *

## Bad Design (Violates LSP)

```java
class Bird {

    public void fly() {
        System.out.println("Bird is flying");
    }
}

class Penguin extends Bird {

    @Override
    public void fly() {
        throw new UnsupportedOperationException("Penguins cannot fly");
    }
}
```

Problem:

```java
Bird bird = new Penguin();
bird.fly(); // Runtime error
```

* * *

# Better Design (Follows LSP)

Separate behavior properly.

```java
abstract class Bird {

    abstract void move();
}

class FlyingBird extends Bird {

    @Override
    void move() {
        System.out.println("Flying");
    }
}

class Penguin extends Bird {

    @Override
    void move() {
        System.out.println("Swimming");
    }
}
```

Now:

✔ All birds move

✔ Behavior remains valid

✔ No broken expectations

* * *

# 🧠 Key Rules of LSP

Follow these rules while designing subclasses:

* * *

## 1️⃣ Do Not Change Expected Behavior

Subclass must behave like parent.

Bad:

```java
throw new UnsupportedOperationException();
```

* * *

## 2️⃣ Do Not Strengthen Preconditions

Subclass should **not require stricter input rules**.

* * *

## 3️⃣ Do Not Weaken Postconditions

Subclass must return valid results.

* * *

## 4️⃣ Preserve Parent Contracts

Follow the parent class expectations.

* * *

# ⚠️ Signs You're Violating LSP

Watch for:

🚩 Many `instanceof` checks

🚩 Overridden methods throwing exceptions

🚩 Unexpected output changes

🚩 Subclass restricting parent behavior

🚩 Breaking polymorphism

* * *

# Benefits of Following LSP

✔ Predictable inheritance

✔ Cleaner architecture

✔ Fewer runtime errors

✔ Better extensibility

✔ Easier maintenance

* * *

# Summary

The **Liskov Substitution Principle (LSP)** ensures that:

✔ Subclasses behave correctly

✔ Parent objects can be replaced safely

✔ Polymorphism works properly

✔ Software becomes maintainable

* * *

# 🧩 One-Line Takeaway

👉 **"If a subclass cannot replace its parent without breaking behavior, the design violates LSP."**

* * *
