The super Keyword in Java: Calling Parent Class Methods

In Java, working with inheritance means that sometimes you want to access or invoke members of a parent class from within a child class. The super keyword is designed exactly for that purpose. Over the years, I’ve found that super is an essential tool whenever I want to extend or customize behavior in subclasses without losing what the parent class already offers.

In this article, I’ll walk you through how the super keyword works, when to use it, and practical examples that demonstrate its value in day-to-day Java programming. You’ll come away with a clear understanding of how to call parent class methods effectively and how to leverage super for clean, maintainable code.

What the super Keyword Does

The super keyword in Java serves as a reference to the immediate parent class object of the current instance. When you invoke a method or constructor with super, you’re explicitly telling Java to use the version from the parent class rather than the overridden version in the subclass.

This becomes especially important when you want to add functionality on top of the parent class’s behavior or when you need to access variables or constructors defined in the parent class.

To put it simply, super gives you a way to call back to the original, inherited code, even if you have overridden or hidden that code in your subclass.

Using super to Call Parent Class Methods

One of the most common uses of super is to call an overridden method from a parent class.

Imagine you have a class called Animal with a method makeSound():

java public class Animal {
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

Now, if you create a subclass called Dog and override makeSound():

java public class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}

By default, calling makeSound() on a Dog object will print “Bark.” But what if you want to include the generic animal sound as well? You can use super inside the overridden method:

java public class Dog extends Animal {
    @Override
    public void makeSound() {
        super.makeSound();  // Calls Animal's makeSound()
        System.out.println("Bark");
    }
}

When you create a Dog and call makeSound(), the output will be:

rust Some generic animal sound
Bark

I use this pattern all the time when extending classes that provide core behavior. It’s a clean way to keep the base functionality while adding subclass-specific details.

Using super to Access Parent Class Variables

In addition to methods, super can also access fields in the parent class. This is useful when a subclass defines a field with the same name as the parent, which hides the parent’s field.

Consider this example:

java public class Vehicle {
    public String type = "Vehicle";
}

public class Car extends Vehicle {
    public String type = "Car";

    public void printTypes() {
        System.out.println(type);        // Refers to Car's type
        System.out.println(super.type);  // Refers to Vehicle's type
    }
}

Creating a Car and calling printTypes() will print:

nginx Car
Vehicle

Here, super.type explicitly accesses the hidden variable in the parent class. In my experience, this technique comes in handy when subclass fields shadow parent fields but you still need to refer to the original data.

Calling Parent Class Constructors with super()

Another crucial use of super is calling the parent class constructor from the subclass constructor.

If the parent class has parameterized constructors or initialization logic, it’s a good practice to invoke those constructors to ensure the object is set up correctly.

Here’s an example:

java public class Person {
    private String name;

    public Person(String name) {
        this.name = name;
        System.out.println("Person constructor called");
    }
}

Now, a subclass Employee that extends Person:

java public class Employee extends Person {
    private int employeeId;

    public Employee(String name, int employeeId) {
        super(name);  // Calls Person's constructor
        this.employeeId = employeeId;
        System.out.println("Employee constructor called");
    }
}

When you create a new Employee:

java Employee emp = new Employee("Alice", 1234);

The output will be:

kotlin Person constructor called
Employee constructor called

This shows that the parent class constructor executes before the subclass constructor. I always recommend calling the appropriate superclass constructor explicitly using super() when your parent class has constructors that perform essential setup.

Important Rules About Using super()

  • The call to super() in a constructor must be the very first statement.
  • If you don’t explicitly call super() or super(args) in a constructor, Java automatically inserts a no-argument call to the superclass constructor.
  • If the parent class does not have a no-argument constructor, you must explicitly call one of its constructors using super(args).

I’ve run into issues in the past where forgetting to call the correct superclass constructor led to compilation errors. Keeping these rules in mind helps avoid such pitfalls.

Differentiating Between this() and super()

Both this() and super() are used to call constructors, but they differ in what they invoke:

  • this() calls another constructor in the same class.
  • super() calls a constructor in the immediate parent class.

For example:

java public class Person {
    public Person() {
        System.out.println("Default Person constructor");
    }

    public Person(String name) {
        System.out.println("Person constructor with name: " + name);
    }
}

public class Employee extends Person {
    public Employee() {
        this("Unknown");  // Calls Employee(String)
        System.out.println("Default Employee constructor");
    }

    public Employee(String name) {
        super(name);  // Calls Person(String)
        System.out.println("Employee constructor with name: " + name);
    }
}

This technique allows chaining constructors and invoking parent constructors in a controlled way.

Using super in Overridden Methods

Sometimes, subclass methods override a parent method completely, but part of the functionality still relies on the parent’s implementation. Calling super inside such methods enables code reuse.

For instance, suppose you have a base class that processes data, and a subclass that extends the processing:

java public class Processor {
    public void process() {
        System.out.println("Base processing");
    }
}

public class AdvancedProcessor extends Processor {
    @Override
    public void process() {
        super.process();  // Calls Processor's process()
        System.out.println("Advanced processing");
    }
}

Using this pattern, I can enhance existing behavior without rewriting everything from scratch.

When Not to Use super()

While super is useful, it’s important not to overuse it. Here are situations where it’s best avoided:

  • Avoid calling super repeatedly in complex inheritance chains; it can make the code harder to follow.
  • Don’t rely on super to access private members of the parent class private members are not visible to subclasses.
  • When designing classes for flexibility and polymorphism, excessive super calls might indicate tight coupling.

In some projects, I refactor code to reduce reliance on super when it starts to compromise encapsulation or extendibility.

Practical Example: Shape and Rectangle

Let’s explore a complete example to see super in action.

java public class Shape {
    protected String color;

    public Shape(String color) {
        this.color = color;
    }

    public void draw() {
        System.out.println("Drawing shape with color: " + color);
    }
}

public class Rectangle extends Shape {
    private int width;
    private int height;

    public Rectangle(String color, int width, int height) {
        super(color);  // Calls Shape constructor
        this.width = width;
        this.height = height;
    }

    @Override
    public void draw() {
        super.draw();  // Calls Shape's draw method
        System.out.println("Drawing rectangle of width " + width + " and height " + height);
    }
}

Creating and using Rectangle:

java Rectangle rect = new Rectangle("Red", 10, 20);
rect.draw();

Output:

arduino Drawing shape with color: Red
Drawing rectangle of width 10 and height 20

In this example, super enables the subclass to reuse the base drawing behavior while extending it with rectangle-specific details.

Accessing Grandparent Class Members

By default, super accesses only the immediate parent. If your inheritance chain is deeper, you can only call methods or variables from the direct superclass using super.

If you want to call methods higher up in the hierarchy, you have to structure your overrides carefully or add methods in intermediate classes to expose those calls.

This is something I consider carefully when designing class hierarchies, aiming to keep them as shallow as practical.

Common Errors and How to Fix Them

Forgetting to Call super() in Constructors

If the parent class lacks a no-argument constructor and you fail to call one explicitly, you’ll get a compile-time error.

Solution: Always check parent class constructors and use super() accordingly.

Trying to Access Private Parent Members

super cannot be used to access private fields or methods. If you attempt it, you’ll get errors or unexpected behavior.

Solution: Use protected or public access modifiers for members you want subclasses to access.

Misplacing super() Calls in Constructors

Calling super() after other statements in a constructor results in a compile error.

Solution: Always place super() as the first line in the constructor.

Final Thoughts on Using super

Mastering the super keyword is crucial for writing effective Java programs that utilize inheritance properly. It gives you control to build on existing functionality cleanly, avoid code duplication, and maintain clear relationships between classes.

When I design new classes, I always think about how subclasses might override or extend methods, and whether they will need to call super to preserve or augment behavior. This mindset helps produce flexible, reusable code that can evolve over time.

Remember, super is a powerful but simple tool: it connects child and parent classes in a way that maintains a clean inheritance structure. Use it wisely, and you’ll find it indispensable in your Java programming journey.

Similar Posts