10 · 32 · 5 min
Two words one letter apart
They sound alike, mean opposite things, and get confused constantly.
Overloading: same name, different parameters
void print(int n) { }
void print(String s) { }
void print(int n, int m) { }Which one runs is decided at compile time from the declared types of the arguments. The methods are unrelated except for sharing a name, and it is really a naming convenience.
Overriding: same signature, different class
class Animal { String speak() { return "..."; } }
class Dog extends Animal {
@Override String speak() { return "woof"; }
}Which one runs is decided at run time from the actual object. This is what makes polymorphism work.
| Overloading | Overriding | |
|---|---|---|
| Signature | must differ | must match |
| Relationship | same class, or inherited | subclass replaces parent |
| Resolved | compile time | run time |
| Called | static binding | dynamic dispatch |
The trap
class Base {
void handle(Object o) { System.out.println("object"); }
}
class Derived extends Base {
void handle(String s) { System.out.println("string"); }
}
Base b = new Derived();
b.handle("hello"); // prints "object"Derived did not override anything. A different parameter type makes it an overload, so Base still has only its Object version, and the declared type of b is Base, so that is what is called.
This is exactly what @Override prevents. Annotating the method would have failed to compile with "method does not override", and that is why the annotation is worth writing every time even though it is optional.
Overloading with null and numbers
print(null); // ambiguous if both String and Integer versions existOverload resolution with autoboxing, varargs and widening is genuinely complicated, and overloads that differ only in ways the compiler must reason hard about are best avoided. Two clearly named methods beat two overloads that require a language lawyer.
In languages without it
Python has no overloading: a later definition simply replaces an earlier one. The idiomatic equivalent is default arguments or inspecting the type inside a single function. Overriding works normally, because it is about the object rather than about signatures.