Java super Method: Calling Parent Class Methods
java super method: Learn how the super keyword calls parent class methods, constructors, and fields in Java, including overriding behavior, interface defaults, and com...
When a subclass overrides a method, the parent implementation is still available through the super keyword. The java super method pattern lets you call the parent class version of an overridden method from inside the subclass, which is essential when the subclass needs to extend rather than replace the parent's behavior.
The Basic Syntax of super.method()
The syntax is straightforward: inside an instance method of a subclass, super.methodName(arguments) invokes the method as defined in the immediate parent class. The method must be accessible—public, protected, or package-private if the subclass is in the same package—and it must be an instance method rather than a static one.
public class Animal { public String describe() { return "An animal"; } } public class Dog extends Animal { @Override public String describe() { return super.describe() + " that barks"; } }
When describe() is called on a Dog instance, the method first invokes super.describe() to get the parent's string, then appends additional detail. Without super, the subclass would have no way to reach the parent's implementation once it has been overridden.
The super keyword only works within an instance method or constructor of the subclass. It cannot be used in a static context because there is no instance to resolve the parent reference against.
How super Works with Method Overriding
Method overriding in Java is polymorphic: the runtime type of the object determines which method implementation runs, regardless of the reference type. When a subclass overrides a method, the parent's version is shadowed for external callers. super is the only mechanism that lets the subclass explicitly reach back to that shadowed implementation.
Consider a logging scenario where the parent establishes a base format and the child adds context:
public class BaseLogger { protected void log(String message) { System.out.println("[base] " + message); } } public class RequestLogger extends BaseLogger { private final String requestId; public RequestLogger(String requestId) { this.requestId = requestId; } @Override protected void log(String message) { super.log("[request " + requestId + "] " + message); } }
The parent's log method handles the base formatting, and the child prepends request context before delegating upward. This pattern keeps the formatting logic in one place while allowing subclasses to add their own concerns.
One important detail: super does not skip the immediate parent. If a three-level hierarchy exists—Grandparent, Parent, and Child—and Child calls super.method(), it executes Parent's implementation. If Parent does not override the method, the call resolves to Grandparent's implementation. The super keyword always resolves against the immediate parent class first, then follows normal inheritance lookup rules.
Calling the Parent Constructor with super()
The super() call is a related but distinct use of the keyword. It invokes the parent class constructor and must be the first statement in a subclass constructor. If you do not write an explicit super() call, the compiler inserts a no-argument super() automatically—but only if the parent has an accessible no-argument constructor.
public class Vehicle { private final String registration; public Vehicle(String registration) { this.registration = registration; } } public class Car extends Vehicle { private final int doors; public Car(String registration, int doors) { super(registration); this.doors = doors; } }
If Vehicle only had a constructor requiring registration, the Car class would fail to compile without the explicit super(registration) call, because the compiler cannot synthesize a call to a constructor that does not exist. This is a common compile-time error when a parent class adds a parameterized constructor and subclasses are not updated.
The super() call must appear before any other statement in the constructor. This restriction exists because the parent portion of the object must be initialized before the subclass instance fields are set.
Accessing Parent Fields with super
Beyond methods and constructors, super can also access fields declared in the parent class. This matters when a subclass declares a field with the same name as one in the parent—a situation called field shadowing.
public class Account { protected String type = "generic"; } public class SavingsAccount extends Account { private String type = "savings"; public String describeType() { return "Parent type: " + super.type + ", child type: " + type; } }
Field shadowing is generally discouraged because it makes the code harder to follow, but when you encounter it in existing code, super.fieldName is the way to access the parent's version. Unlike methods, fields are resolved at compile time based on the reference type, not at runtime based on the object's actual type.
Interface Default Methods and super
Since Java 8, super can also be used to call a default method from an interface. The syntax differs from class-based calls: you must qualify super with the interface name.
public interface Auditable { default String auditInfo() { return "audited"; } } public class Order implements Auditable { @Override public String auditInfo() { return "order: " + Auditable.super.auditInfo(); } }
This is necessary when a class implements multiple interfaces that declare the same default method, or when a class overrides a default method but still needs the interface's base behavior. The InterfaceName.super.method() syntax resolves the method against that specific interface's default implementation.
Common Mistakes When Using super
A few errors appear frequently when developers work with super.
Using super in a static method. The super keyword requires an instance context. A static method has no this reference, so super.method() fails to compile.
Calling super() after another statement. The constructor invocation rule is strict: super() must be the first statement. Placing it after field initialization or any other statement produces a compile error.
Expecting super to bypass the immediate parent. As noted earlier, super resolves against the immediate parent class. If you need to reach a grandparent's implementation directly, you must call through the parent's own super reference—which means the parent must expose a method that does so.
Forgetting that super only reaches accessible members. A private parent method cannot be called with super because private members are not inherited. If the parent method is private, the subclass cannot access it at all, regardless of super.
Runtime Behavior and Performance Considerations
Calling super.method() is not a special runtime operation. The compiler resolves the call to the parent class's method at compile time, and the invocation uses the same virtual dispatch mechanism as any other method call. There is no meaningful performance penalty compared to calling a method directly on this.
The more relevant concern is behavioral. When you call super.method(), the parent method runs with the same this reference—the subclass instance. This means that if the parent method internally calls another overridable method, that call dispatches polymorphically to the subclass's override, not the parent's version.
public class Template { public void execute() { prepare(); run(); } protected void prepare() { System.out.println("base prepare"); } protected void run() { System.out.println("base run"); } } public class CustomTemplate extends Template { @Override protected void prepare() { System.out.println("custom prepare"); } public void runBaseOnly() { super.execute(); } }
When runBaseOnly() calls super.execute(), the execute method runs from Template, but the internal call to prepare() dispatches to CustomTemplate.prepare(), not Template.prepare(). This is the template method pattern in action, and it is a frequent source of surprise for developers who assume super.execute() runs the parent method in complete isolation.
This behavior matters in production code where parent methods are designed as templates. If you intend to invoke only the parent's exact sequence without polymorphic dispatch, you must design the parent methods to be final or restructure the hierarchy—super alone will not prevent dynamic dispatch.
When to Use super vs. Refactoring the Parent
Using super is the right choice when the parent's implementation is genuinely needed as a base layer and the subclass adds focused behavior. The pattern is common in frameworks where base classes provide lifecycle hooks and subclasses extend specific steps.
However, super calls create a coupling between the subclass and the parent's implementation details. If the parent method changes its behavior, subclasses that delegate through super inherit those changes implicitly. When the parent method is large or its behavior is not well documented, consider whether the subclass should instead call a smaller, focused parent method or whether the shared logic should be extracted into a separate helper that both classes use.
A practical guideline: use super.method() when the parent's method is the natural foundation and the subclass adds a thin layer. Prefer composition or extraction when the parent method does too much and the subclass only needs a portion of it. The super keyword is a language feature, not a design mandate—it makes delegation explicit, but it does not make the delegation a good idea in every hierarchy.