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.
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.
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
| Member | Actual Rule |
|---|---|
| final instance method | Inherited but not overrideable |
| private method | Not inherited by subclasses; a subclass with the same name is a new declaration |
| static method | Hidden by class, no instance dynamic dispatch |
| Field | Can occur as name hiding; access depends on the static type of the expression |
| Constructor | Not inherited, nor overrideable |
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); // parentThis 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
@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
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 overloadOverloading 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
class BrokenSender extends MessageSender {
BrokenSender(String message) { super(message); }
// @Override
// protected void send() {} // Cannot shrink public Access permissions
}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
| Term | Decision Phase | Key Basis |
|---|---|---|
| Overloading | Compilation | Method name, parameter count, and type at compilation time |
| Overriding | Runtime dispatch | Selected signature + actual class of the receiver |
| Static hiding | Compilation | Qualifying class or compile-time type of the expression |
| Field hiding | Compilation | Static 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.