Skip to content

VOL 01 / CH 08 / LESSON 03

8.3 Method Overriding, @Override, and super ​

Prerequisites: Inheritance, Method Signatures, and Constructor Chains

Estimated time: 30 minutes

You are ready to continue when: You can determine if an override is valid and distinguish between overriding, overloading, hiding, and non-overridable members

You intended to override send(), but typed snd(). Without additional checks, the compiler will treat it as a new method, and the caller will still execute the parent class version.

1. @Override Lets the Compiler Check Your Intent ​

This page reuses MessageSender from the inheritance lesson but uses the one-argument EmailSender below. Keep the different class versions separate. Its send() method still only prints demonstration messages.

java
class EmailSender extends MessageSender {
    EmailSender(String message) {
        super(message);
    }

    @Override
    public void send() {
        super.send();
        System.out.println("demo email channel");
    }
}

@Override does not perform dynamic dispatch. It checks that the declaration overrides an eligible method in a parent class or interface. Typos in method names, parameter types, or parameter counts will be exposed at compile time. When implementing interface methods, this should also be used.

2. Valid Overriding Requires More Than a Matching Name ​

In Java 17, the following core constraints must be satisfied:

  • The method signature must have an override-equivalent relationship, which can be understood as having the same name and parameter types for beginners;
  • The return type must be the same, or it can be a covariant return type that is allowed;
  • A subclass cannot narrow the access permissions;
  • A subclass cannot declare broader checked exceptions than the parent method;
  • The parent method must be inheritable and not final/static/private.
java
class Factory {
    protected Number create() throws java.io.IOException {
        return 0;
    }
}

class IntegerFactory extends Factory {
    @Override
    public Integer create() {
        return 1;
    }
}

public is more open than protected, and Integer is a subtype of Number. Additionally, the subclass omits the checked exception, so it is legal.

Generic erasure also introduces details such as bridge methods, which will be explored in depth after studying generics. Do not use "the return types must be exactly the same" as an absolute rule.

3. Which Members Do Not Participate in Instance Method Overriding ​

MemberActual Rule
final instance methodInherited but not overrideable
private methodNot inherited by subclasses; a subclass with the same name is a new declaration
static methodHidden by class, no instance dynamic dispatch
FieldCan occur as name hiding; access depends on the static type of the expression
ConstructorNot inherited, nor overrideable
java
class Parent {
    static String kind() { return "parent"; }
    String label = "parent";
}

class Child extends Parent {
    static String kind() { return "child"; }
    String label = "child";
}

Parent value = new Child();
System.out.println(Parent.kind()); // parent
System.out.println(value.label);  // parent

This is precisely why fields are often private, and static methods are called using the class name. Treating them as instance polymorphic can lead to hard-to-read code.

4. super Starts Lookup in the Direct Parent ​

java
@Override
public void send() {
    super.send();
    System.out.println("Subclass supplemental behavior");
}

super.send() is a parent call determined at compile time and will not dynamically dispatch back to the current overridden method. Whether it is called depends on the parent class contract, and not all overrides are required to "first call super". If the parent method specifies necessary invariants, a more robust design might be to set the flow as final, opening only controlled hooks.

In Java 17, a constructor's super(...) call selects a parent constructor and must be its first statement. This is a different use of super from super.send().

5. Overloading and Overriding Select at Different Stages ​

java
class Dispatcher {
    void deliver(MessageSender sender) {
        System.out.println("sender overload");
    }

    void deliver(EmailSender sender) {
        System.out.println("email overload");
    }
}

MessageSender value = new EmailSender("Message");
new Dispatcher().deliver(value); // sender overload

Overloading is resolved at compile time based on the method name, number of parameters, and compile-time type. At runtime, the object is EmailSender, and it does not allow the call to select another overload.

Overriding, on the other hand, occurs after the instance method signature has been selected. The JVM then chooses the most specific overridden implementation based on the actual class of the receiver. It is essential to distinguish between these two stages in order to predict the behavior of mixed "overloading + overriding" code.

Common Compilation Failures ​

java
class BrokenSender extends MessageSender {
    BrokenSender(String message) { super(message); }

    // @Override
    // protected void send() {} // Cannot shrink public Access permissions
}
java
class TypoSender extends MessageSender {
    TypoSender(String message) { super(message); }

    // @Override
    // public void snd() {} // No covering method available
}

Intentional failure scenarios in teaching should be compiled and diagnosed separately, rather than guessed from comments alone.

Professional Quick Reference ​

TermDecision PhaseKey Basis
OverloadingCompilationMethod name, parameter count, and type at compilation time
OverridingRuntime dispatchSelected signature + actual class of the receiver
Static hidingCompilationQualifying class or compile-time type of the expression
Field hidingCompilationStatic type of the expression

Before Continuing ​

Check the signature, covariant return, access level, and checked exceptions of an override. Use javac to reject two small examples: one with a misspelled method name and one with reduced access. Then, predict the fields, static methods, and instance methods separately using Parent value = new Child(), and finally explain that overloading selects the signature first and overriding selects the implementation next.

→ Next: Abstract Classes, Interfaces, and Object

Standard Entry: JLS §8.4.8.1: Instance Method Overriding.

Built with VitePress | Software Systems Atlas