Overloading vs overriding in Java
Overloads are chosen at compile time by declared types; overrides at runtime.
String overload... and Java calls the Object version instead. A bug? No — a matter of timing.Overloading: same name, new parameters
Overloaded methods share a name but differ in their parameter lists (number, types or order). The compiler picks one, using the declared types of the arguments.
void p(Object o) { ... }
void p(String s) { ... }Who gets the call?
What does this print?
class Greeter {
void hi(Object o) {
System.out.print("obj "); }
void hi(String s) {
System.out.print("str "); }
}
void main() {
var g = new Greeter(); Object x = "Ann";
g.hi(x); g.hi("Ann");
}str strobj strobj obj
Show the answer
obj str. x holds a String, but it's declared Object, and overloads are chosen at compile time from declared types. So hi(Object) wins for x.
Compile time vs runtime
Overloading = choosing among *signatures*, at compile time, by declared types. Overriding = choosing among *implementations* of one signature, at runtime, by the object's class. To overload, change the parameters; to override, keep the exact signature.
Both at once
A has f(Object). B extends A overrides f(Object) and adds f(String). With A a = new B();, what does a.f("x") run?
Think about it, then reveal the answer
**B's f(Object).** Step 1, compile time: A only offers f(Object), so that signature is locked in. Step 2, runtime: the object is a B, so B's override of f(Object) runs. B's f(String) was never even considered.
Return type isn't enough
Two methods with the same name and parameters but different return types are not overloads — the compiler reports a duplicate method. A call like size() would give it no way to choose.
int size() { return 1; }
long size() { return 2L; } // error!In real projects
A famous overload trap: on a List<Integer>, list.remove(1) calls remove(int index) and removes the element at position 1 — not the value 1. To remove the value, write list.remove(Integer.valueOf(1)).
Key takeaways
- Overload: different parameters, chosen at compile time
- Override: same signature, chosen at runtime
- Overload choice uses declared argument types
- Methods can't differ only by return type
System.out.println has 10 overloads: no arguments, boolean, char, int, long, float, double, char[], String and Object.
Practice questions
What does this print?
class Printer {
void p(Object o) { System.out.print("Object "); }
void p(String s) { System.out.print("String "); }
}
void main() {
var pr = new Printer();
Object o = "hi";
pr.p(o);
pr.p("hi");
}- String String
- Object String
- Object Object
- String Object
Check your answer
Object String. Overloads are picked at compile time from declared types. o is declared Object, so p(Object) is chosen even though it holds a String.
What does this print?
class A {
void f(Object o) { System.out.print("A.obj"); }
}
class B extends A {
void f(Object o) { System.out.print("B.obj"); }
void f(String s) { System.out.print("B.str"); }
}
void main() {
A a = new B(); a.f("x");
}- B.str
- B.obj
- A.obj
Check your answer
B.obj. At compile time A only offers f(Object), so that signature is chosen. At runtime it dispatches to B's override of f(Object). B's f(String) is never considered.