OOPS Interview Questions · Question 05

What is inheritance, and when does an 'is-a' relationship justify using it?

Interview preparation resource from Gate Smashers.

Interview-ready answer

Inheritance is an object-oriented mechanism in which a derived class extends a base class, forming a subtype relationship. It is appropriate when the derived type is genuinely a specialized form of the base type and can be used wherever the base type is expected without violating its contract. This is the practical meaning of an “is-a” relationship and supports polymorphism. Use inheritance to model a stable shared abstraction, not merely to reuse implementation; for “has-a” or “uses-a” relationships, composition is usually a better fit.

Java examples: inheritance, a contract mismatch, and compositionjava
// Correct "is-a" example
class Animal {
    void eat() {
        System.out.println("eating");
    }
}

class Bird extends Animal {
    void fly() {
        System.out.println("flying");
    }
}

// Potential LSP violation when Rectangle allows independent dimensions
class Rectangle {
    protected int width;
    protected int height;

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

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

    int area() {
        return width * height;
    }
}

class Square extends Rectangle {
    @Override
    void setWidth(int width) {
        this.width = width;
        this.height = width;
    }

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

// Composition for a "has-a" relationship
class Engine {
    void start() {
        System.out.println("engine started");
    }
}

class Car {
    private final Engine engine;

    Car(Engine engine) {
        this.engine = engine;
    }

    void start() {
        engine.start();
    }
}
Understand it clearly

What inheritance means

Inheritance lets a derived class reuse and extend the behavior and accessible members of a base class. It also establishes a type relationship: code written against the base type can work with compatible derived objects.

The exact inheritance rules, such as member visibility, multiple inheritance support, and method-overriding behavior, depend on the programming language.

When “is-a” justifies inheritance

An “is-a” statement is a useful starting point, but it is not sufficient by itself. The derived class must preserve the promises made by the base class so that replacing a base object with a derived object does not break correct client code.

This requirement is commonly expressed by the Liskov Substitution Principle: a subtype should honor the behavioral contract of its supertype.

  • Specialization: The derived type represents a more specific kind of the base type, such as a Bird being an Animal.
  • Substitutability: A derived object can be passed to code expecting the base type without surprising changes in expected behavior.
  • Shared abstraction: The base class defines meaningful common behavior or a common interface that clients need to use polymorphically.
  • Stable semantics: The hierarchy reflects the domain model rather than being created only to avoid duplicated code.

When to prefer composition

Prefer composition when one object contains, uses, or delegates work to another object. Composition keeps responsibilities separate and avoids forcing a subtype relationship where none exists.

Inheritance can couple a derived class to assumptions and changes in the base class. If the goal is only implementation reuse, delegation to a contained object is often more flexible.

  • Has-a relationship: A Car has an Engine, so the Car should contain an Engine rather than extend it.
  • Behavior reuse only: If two classes need the same helper behavior but are not substitutable types, extract that behavior into a separate component.
  • Contract mismatch: Avoid inheritance when overriding a base operation would require changing its expected meaning or restricting valid base-class behavior.

A common contract mismatch

A Square may satisfy the everyday “is-a Rectangle” statement, but it can violate a mutable Rectangle contract if Rectangle allows width and height to change independently. Making Square setters update both dimensions can surprise code that expects those dimensions to remain independent.

The issue is not the names alone; it is whether the base-class contract was designed so that Square remains substitutable.

Quick comparison
BasisInheritanceComposition
Relationship modeledAn “is-a” subtype relationship.A “has-a” or “uses-a” relationship.
Reuse approachA derived class extends a base class.An object delegates work to contained or referenced components.
Best fitA stable shared abstraction with substitutable specialized types.Flexible reuse or collaboration between separate responsibilities.
Main riskA derived class may break the base-class contract or become tightly coupled to base-class changes.May require explicit delegation or additional component wiring.