Constructor Chaining

About

Constructor Chaining in Java is a technique where one constructor calls another constructor within the same class or from its superclass. This ensures that object initialization is consistent and avoids code duplication.

Every time an object is created, a constructor is invoked to initialize it. In cases where multiple constructors are defined (known as constructor overloading), constructor chaining helps manage this initialization efficiently by centralizing common setup logic.

Constructor chaining uses:

  • this() to call a constructor in the same class

  • super() to call a constructor in the immediate parent class

It is particularly useful in object-oriented design where classes are extended and constructors must work together to initialize fields across multiple levels of inheritance.

Types of Constructor Chaining

Constructor chaining in Java can be categorized into two main types:

1. Chaining Within the Same Class (this())

This type involves calling one constructor from another in the same class using the this() keyword.

Purpose: To reuse code and avoid repeating the same logic in multiple constructors.

class Book {
    String title;
    int pages;

    Book() {
        this("Unknown", 0);  // Calls the two-arg constructor
    }

    Book(String title, int pages) {
        this.title = title;
        this.pages = pages;
    }
}

Here, the no-argument constructor delegates object initialization to the parameterized constructor.

2. Chaining Between Superclass and Subclass (super())

This type involves a subclass constructor calling a constructor from its immediate superclass using the super() keyword.

Purpose: To ensure that the parent class is properly initialized before the subclass.

class Animal {
    Animal(String type) {
        System.out.println("Animal: " + type);
    }
}

class Dog extends Animal {
    Dog() {
        super("Dog");  // Calls the superclass constructor
        System.out.println("Dog is created");
    }
}

In this example, when a Dog object is created, the Animal constructor runs first, then the Dog constructor completes.

Why Constructor Chaining Is Important

Constructor chaining is more than just a coding shortcut it's a fundamental practice in object-oriented programming that leads to cleaner, more efficient, and more maintainable code.

1. Eliminates Redundancy

Instead of repeating the same initialization code across multiple constructors, constructor chaining allows us to write it once and reuse it via this() or super().

class User {
    String name;
    int age;

    User() {
        this("Unknown", 0);  // No duplication here
    }

    User(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

2. Centralizes Initialization Logic

Constructor chaining helps group shared setup logic in a single constructor. If we ever need to modify the initialization process, we only have to change it in one place.

3. Improves Readability and Maintainability

Chained constructors clearly show the dependency and order of initialization. This makes code easier to understand and modify.

4. Ensures Proper Superclass Initialization

In inheritance hierarchies, constructor chaining (via super()) guarantees that the base class is initialized before any subclass logic runs — a key rule in object-oriented design.

5. Supports Flexible Object Creation

Chained constructors provide multiple ways to create objects using different parameters, while ensuring that all objects are initialized consistently.

Rules of Constructor Chaining

Constructor chaining allows one constructor to call another either in the same class or in a superclass to streamline object initialization. However, Java enforces specific rules to ensure this process is structured and unambiguous.

Below are the key rules we must follow when implementing constructor chaining:

1. Constructor Call Must Be the First Statement

Whether using this() or super(), the constructor call must be the first line in the constructor body.

class A {
    A() {
        this(10);        // Correct
        System.out.println("Hello");  // After constructor call
    }

    A(int x) {
        System.out.println("Value: " + x);
    }
}
// Invalid
class B {
    B() {
        System.out.println("Start"); // Error: must call this() or super() first
        this(5);                     // Compilation error
    }

    B(int x) { }
}

2. Only One Constructor Call Allowed

Inside any constructor, we can only make one constructor call either this() or super() not both.

class C {
    C() {
        // this(10);   // If used
        // super();    // This would cause a compile-time error
    }
}

We must choose either to chain to another constructor of the same class or to the superclass not both.

3. Constructor Chaining Cannot Be Recursive

Constructors cannot call themselves directly or indirectly in a loop. This will result in a StackOverflowError.

class D {
    D() {
        this();  // Infinite loop → Runtime error
    }
}

The chaining must eventually reach a constructor that does not call another constructor to stop the chain.

4. Superclass Constructor Is Called Implicitly If Not Specified

If we do not explicitly call super() or this() in a constructor, Java automatically inserts a call to the no-argument constructor of the superclass.

class E {
    E() {
        System.out.println("E constructor");
    }
}

class F extends E {
    F() {
        // Java automatically adds: super();
        System.out.println("F constructor");
    }
}

If the superclass does not have a no-arg constructor, and the subclass does not explicitly call a different one, the code will not compile.

5. Constructors Are Not Inherited

Although constructors are not inherited like methods, we can still use constructor chaining with super() to invoke them explicitly.

6. The Constructor Chain Ends at a Constructor That Does Not Chain Further

Constructor chaining must eventually reach a constructor that does not call another constructor. This terminal constructor usually performs the final initialization.

class G {
    G() {
        this(100);  // Chains to G(int)
    }

    G(int x) {
        System.out.println("Final constructor with x = " + x);
    }
}

Examples

Example 1: Basic Constructor Chaining in Inheritance

class Animal {
    Animal() {
        System.out.println("Animal constructor");
    }
}

class Dog extends Animal {
    Dog() {
        super();  // Optional, added automatically by Java
        System.out.println("Dog constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Dog();
    }
}

Output

Animal constructor
Dog constructor

Example 2: Parameterized Constructor in Superclass

class Animal {
    Animal(String type) {
        System.out.println("Animal type: " + type);
    }
}

class Dog extends Animal {
    Dog() {
        super("Dog");  // Must call explicitly; no default constructor in Animal
        System.out.println("Dog constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Dog();
    }
}

Output

Animal type: Dog
Dog constructor

Example 3: Multiple Levels of Inheritance

class LivingBeing {
    LivingBeing() {
        System.out.println("LivingBeing constructor");
    }
}

class Animal extends LivingBeing {
    Animal() {
        System.out.println("Animal constructor");
    }
}

class Cat extends Animal {
    Cat() {
        System.out.println("Cat constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Cat();
    }
}

Output

LivingBeing constructor
Animal constructor
Cat constructor

Example 4: Constructor Chaining Across Levels with Parameters

class Vehicle {
    Vehicle(String type) {
        System.out.println("Vehicle: " + type);
    }
}

class Car extends Vehicle {
    Car(String model) {
        super("Car");  // calling Vehicle(String)
        System.out.println("Model: " + model);
    }
}

class ElectricCar extends Car {
    ElectricCar(String model, int battery) {
        super(model);  // calling Car(String)
        System.out.println("Battery: " + battery + "%");
    }
}

public class Test {
    public static void main(String[] args) {
        new ElectricCar("Tesla Model 3", 85);
    }
}

Output

Vehicle: Car
Model: Tesla Model 3
Battery: 85%

Example 5: Calling Overloaded Constructors with this() and super()

class Employee {
    Employee(String name) {
        System.out.println("Employee: " + name);
    }
}

class Manager extends Employee {
    Manager() {
        this("Default Manager");
    }

    Manager(String name) {
        super(name);
        System.out.println("Manager constructor");
    }
}

public class Test {
    public static void main(String[] args) {
        new Manager();
    }
}

Output

Employee: Default Manager
Manager constructor

Example 6: Real-World Modeled Example (Banking)

class Account {
    Account(String accountHolder) {
        System.out.println("Account for: " + accountHolder);
    }
}

class SavingsAccount extends Account {
    SavingsAccount(String accountHolder, double interestRate) {
        super(accountHolder);
        System.out.println("Interest Rate: " + interestRate + "%");
    }
}

public class Test {
    public static void main(String[] args) {
        new SavingsAccount("Pranay", 5.0);
    }
}

Output

Account for: Pranay
Interest Rate: 5.0%

Common Errors and Misconceptions

Using this() and super() Together

Not allowed. Only one constructor call is permitted and must be the first line.

class A {
    A() {
        this();      // OK if only one constructor is called
        super();     // Error: Cannot use both
    }
}

Recursive Constructor Calls

Calling constructors in a cycle will cause a StackOverflowError.

class Loop {
    Loop() {
        this();  // Infinite recursion
    }
}

Last updated