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 classsuper()
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()
)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()
)
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
Even if super()
is not written explicitly, Java inserts it automatically if the superclass has a no-arg 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
Since Animal
has no no-arg constructor, the Dog
class must explicitly call super("Dog")
.
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
Constructors are called in top-down order: superclass first, then subclass.
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%
Constructor calls flow from ElectricCar
→ Car
→ Vehicle
.
Example 5: Calling Overloaded Constructors with this()
and super()
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
First this("Default Manager")
is called, which then calls super(name)
.
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
this()
and super()
TogetherNot 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