How does overloading differ from overriding, especially in how each is dispatched?
answer
- Overload = different params, compile time, static type
- Override = same signature, runtime, receiver's real type
- Overriding needs inheritance; overloading doesn't
- @Override guards overriding only
- Overload chosen by declared type, not runtime type
basics
~20 sOverloading is same method name with different parameter lists, chosen by the compiler from the argument types (compile time). Overriding is a subclass replacing a superclass method with the same signature, chosen by the object's real type at runtime.
solid answer
~50 sOverloading and overriding are different mechanisms that beginners often conflate. Overloading means several methods share a name but have different parameter lists; the compiler selects which to call from the static (declared) types of the arguments at compile time — this is static binding. It does not require inheritance. Overriding means a subclass declares a method with the same name and same parameter list (and a compatible return type) as a superclass method, replacing its behavior; the JVM selects which implementation to run from the actual runtime type of the receiver object — this is dynamic dispatch and is the basis of polymorphism. A subtle interaction: when you both override and overload, the compiler first picks the overload by static argument types, then the runtime picks the override of that chosen overload. The @Override annotation guards overriding (catches signature typos); it has no meaning for overloading.
code
java · 12 linesclass Base {
void greet(Object o) { System.out.println("Base.greet(Object)"); }
}
class Sub extends Base {
@Override void greet(Object o) { System.out.println("Sub.greet(Object)"); } // OVERRIDE
void greet(String s) { System.out.println("Sub.greet(String)"); } // OVERLOAD
}
Base b = new Sub();
Object x = "hi";
b.greet(x); // OVERLOAD picked by static type Object -> greet(Object);
// OVERRIDE picks Sub's impl -> prints "Sub.greet(Object)"go deeper
States the one-line difference: overloading = different parameters in the same class; overriding = subclass redefines a method.
Explains compile-time vs runtime binding and that overriding needs inheritance while overloading does not.
Works through the static-type-selects-overload trap and the equals(Object) overload pitfall, and explains combined overload+override dispatch.
Relates overriding to Liskov substitution/polymorphism and overloading to static API design, and sets conventions (mandatory @Override, prefer distinct names over overlapping overloads).
### Two mechanisms, one easy confusion **Overloading** and **overriding** both involve methods with the same *name*, which is why they get mixed up. But they answer different questions and bind at different times. ### Overloading (compile-time, static binding) - **Definition:** multiple methods in scope with the **same name** but **different parameter lists** (different number/types/order of parameter types). - **Selection:** the **compiler** chooses one, using the **static (declared) types** of the arguments at the call site, via the three-phase algorithm (widen -> box -> vararg). The chosen method is **hard-wired into the bytecode**. - **Inheritance:** not required — overloads can all live in one class. - **Signature rule:** return type and parameter names are excluded; you cannot overload by return type alone. ### Overriding (runtime, dynamic dispatch) - **Definition:** a **subclass** declares a method with the **same name and same parameter list** as an accessible superclass (or interface) method, providing a new implementation. - **Rules:** the return type must be the same or a **covariant** subtype; access must be the **same or wider** (you cannot reduce visibility); the override must not throw **broader** checked exceptions; `static`, `private`, and `final` methods cannot be overridden (a same-signature `static` method in a subclass is *hiding*, not overriding). - **Selection:** the **JVM** chooses the implementation at runtime from the **actual type of the receiver object** (the object the method is invoked on), not its declared type. This dynamic dispatch is the engine of **polymorphism**. - **@Override:** an optional but recommended annotation; the compiler verifies the method really does override something, catching silent mistakes (e.g. `equals(MyType)` instead of `equals(Object)`, which is actually an *overload*, not an override — a classic bug). ### Side-by-side | Aspect | Overloading | Overriding | |---|---|---| | Method name | same | same | | Parameter list | **different** | **same** | | Return type | irrelevant to selection; can't differ alone | same or covariant | | Binding time | **compile time** (static) | **runtime** (dynamic) | | Chosen by | static types of arguments | runtime type of receiver | | Needs inheritance | no | yes | | Polymorphism | no | yes | | Annotation | (none) | `@Override` | ### The classic trap ``` Object o = "hello"; System.out.println(o.getClass()); // class java.lang.String (runtime type) ``` Now consider overloading by static type: ``` void p(Object o) { System.out.println("Object"); } void p(String s) { System.out.println("String"); } Object ref = "hi"; p(ref); // prints "Object" -- overload chosen by ref's STATIC type (Object) ``` Even though `ref` holds a `String` at runtime, the **overload** is fixed by its **declared** type `Object`. Overloading ignores runtime types. Overriding would have used the runtime type. ### Combined: overload + override together If a base class has `process(Animal)` and `process(Dog)` (overloads), and subclasses override `process(Animal)`, then for a call `processor.process(someAnimal)` the **compiler** first selects the `process(Animal)` overload by static type, and **then** the **runtime** selects the overriding implementation of `process(Animal)`. Two decisions, two phases, two binding times. ### Why it matters - The `equals` bug: writing `public boolean equals(MyClass other)` overloads `Object.equals(Object)` instead of overriding it; collections still call `equals(Object)` and get the wrong behavior. `@Override` would have flagged it. - Performance/clarity: overloading is a static convenience; overriding enables substitutability (Liskov) and is the heart of OO polymorphism.
- If a variable's declared type is Object but it holds a String, which overload is chosen?The Object overload. Overloading is resolved at compile time from the declared (static) type, so the runtime String type is ignored. Only overriding would react to the runtime type.
- Is writing equals(MyType other) overriding or overloading Object.equals?Overloading. Object.equals takes Object, so a parameter of MyType creates a new overload, not an override. Collections call equals(Object) and won't use it. Adding @Override would make the compiler reject the mistake.
Overloading is choosing a tool from a labeled drawer by the shape of the part in your hand (decided up front). Overriding is calling 'the manager' and whoever actually holds that role today answers (decided live).
saying these in an interview costs you the question
- Saying overloading is resolved by the runtime type of arguments
- Claiming you can override by changing the parameter list
- Thinking @Override applies to overloads
- Believing overloading is a form of polymorphism