Favoring Composition Over Inheritance In Java With Examples

Last Updated : 3 Sep, 2026

Composition and inheritance are two important approaches for achieving code reuse and designing relationships between classes in Java. Although inheritance is useful when a genuine subtype relationship exists, composition is often preferred because it provides greater flexibility, reduces coupling, and many more.

  • Inheritance promotes code reuse through superclass-subclass relationships.
  • Composition promotes code reuse through object composition and delegation.
  • Composition generally provides more flexibility than inheritance.

Inheritance

Inheritance is a Java mechanism in which a subclass acquires accessible fields and methods from a superclass. It is used to represent an "is-a" relationship and is implemented using the extends keyword.

  • Supports method overriding and runtime polymorphism.
  • Allows common behavior to be defined in a superclass and specialized in subclasses.
  • A Java class can directly extend only one class.
Java
class Person {
    // Private fields for encapsulation
    private String name;
    private int age;

    // Constructor to initialize fields
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter for name
    public String getName() {
        return name;
    }

    // Setter for name
    public void setName(String name) {
        this.name = name;
    }

    // Getter for age
    public int getAge() {
        return age;
    }

    // Setter for age
    public void setAge(int age) {
        this.age = age;
    }
}

class Employee extends Person {
    // Private field for encapsulation
    private int salary;

    // Constructor to initialize fields
    public Employee(String name, int age, int salary) {
        super(name, age); // Call superclass constructor
        this.salary = salary;
    }

    // Getter for salary
    public int getSalary() {
        return salary;
    }

    // Setter for salary
    public void setSalary(int salary) {
        this.salary = salary;
    }

    // Method to display employee information
    public void displayInfo() {
        System.out.println("Name: " + getName()); 
        System.out.println("Age: " + getAge()); 
        System.out.println("Salary: " + salary);  
    }
}

public class Main {
    public static void main(String[] args) {
        // Create an Employee object
        Employee emp = new Employee("Geek1", 30, 50000);

        // Display employee information
        emp.displayInfo();
    }
}

Output
Name: Geek1
Age: 30
Salary: 50000

Explanation: In this example, the Employee class inherits from Person and adds a new property, salary.

Composition

Composition is a design approach in which a class contains references to objects of other classes and uses them to provide functionality. It represents a "has-a relationship".

  • Components can be replaced without changing the containing class.
  • Promotes loose coupling when the class depends on interfaces or abstractions.
  • Allows different implementations to be combined at runtime.

For example: A Car has an Engine. Instead of inheriting engine behavior, the Car class can contain an Engine object and delegate engine-related operations to it.

Java
class Address {
    // Private fields for encapsulation
    private String street;
    private String city;
    private String zipCode;

    // Constructor to initialize fields
    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }

    // Getter for street
    public String getStreet() {
        return street;
    }

    // Setter for street
    public void setStreet(String street) {
        this.street = street;
    }

    // Getter for city
    public String getCity() {
        return city;
    }

    // Setter for city
    public void setCity(String city) {
        this.city = city;
    }

    // Getter for zipCode
    public String getZipCode() {
        return zipCode;
    }

    // Setter for zipCode
    public void setZipCode(String zipCode) {
        this.zipCode = zipCode;
    }

    // Method to display address (delegation)
    public void displayAddress() {
        System.out.println(street + ", " + city + ", " + zipCode);
    }
}

class Person {
    // Private fields for encapsulation
    private String name;
    private Address address;

    // Constructor to initialize fields
    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // Getter for name
    public String getName() {
        return name;
    }

    // Setter for name
    public void setName(String name) {
        this.name = name;
    }

    // Getter for address
    public Address getAddress() {
        return address;
    }

    // Setter for address
    public void setAddress(Address address) {
        this.address = address;
    }

    // Method to display person information
    public void displayInfo() {
        System.out.println("Name: " + name);
        System.out.print("Address: ");
        address.displayAddress(); 
    }
}

public class Main {
    public static void main(String[] args) {
        // Create an Address object
        Address addr = new Address("123 Main St", "Springfield", "12345");

        // Create a Person object
        Person person = new Person("Geek1", addr);

        // Display person information
        person.displayInfo();
    }
}

Output
Name: Geek1
Address: 123 Main St, Springfield, 12345

Explanation: In this example, the Person class uses composition to include an Address object. This creates a "has-a" relationship between Person and Address.

Why Favor Composition Over Inheritance?

Composition is often favored over inheritance because it provides greater flexibility when behavior needs to change independently.

  • Loose Coupling: A class can depend on an interface or component instead of being tightly coupled to a superclass.
  • Greater Flexibility: Components can be replaced or changed without creating new subclasses.
  • Runtime Behavior Changes: A class can work with different implementations during its lifetime.
  • Better Reusability: The same component can be used by multiple classes.
  • Better Testability: Components can be replaced with test implementations or mocks.
  • Avoids Large Inheritance Hierarchies: Multiple independently varying behaviors do not require a separate subclass for every combination.
  • Encapsulation: Implementation details can remain inside the composed component.
  • Easier Maintenance: Changes to a component generally do not require changes to the inheritance hierarchy.

Composition vs Inheritance

FeatureInheritanceComposition
RelationshipIs-aHas-a
Main mechanismextendsObject reference
CouplingGenerally tighterGenerally looser
Behavior reuseThrough inheritanceThrough delegation
FlexibilityLess flexible when behavior variesMore flexible
Runtime replacementNot normally possible for the inherited implementationComponent can be replaced
Multiple independent behaviorsCan lead to many subclassesBehaviors can be combined
TestingCan be more difficult with tightly coupled hierarchiesComponents can be substituted easily
ExampleDog extends AnimalCar has Engine
Comment