Method Overriding
Overriding is when a child class provides its own version of a method it inherited. The call stays the same; the behaviour depends on the actual object. It is runtime polymorphism.
On this page · 5 min · Beginner
On this page
- In simple words
- Learning objectives
- Explanation
- Syntax
- Code example
- Step by step
- Real-world use
- Common mistakes
- Best practices
- Remember this
- Practice exercise
- Mini quiz
- Related topics
- My notes
- Reading time
- 5 min
- Difficulty
- Beginner
- Updated
- Oct 28, 2026
Related tutorials
In simple words#
A family recipe (parent method) that a child cooks their own way. The child keeps the name but replaces the steps.
- Overriding
- Child replaces a parent method.
- virtual
- Parent allows overriding.
- override
- Child provides the new version.
Learning objectives#
- Override an inherited method
- Extend parent behaviour with super / base
- Explain overriding vs overloading
- Override toString for readable output
Explanation#
A subclass overrides a method by declaring one with the same name and parameters. When the method is called, the version of the actual object runs — decided at runtime.
Override to replace behaviour completely, or call super.method() (JavaScript) / base.Method() (C#) inside the override to extend it.
In C#, the parent method must be marked virtual (or abstract) and the child must say override. In JavaScript every method can be overridden.
Overloading = same name, different parameters, chosen at compile time. Overriding = same name, same parameters, different class, chosen at runtime.
Overriding toString() (ToString() in C#) controls how an object prints — very useful in logs and template strings.
Syntax#
class Notification
{
public virtual string Channel() => "generic";
public override string ToString() => quot;Notification via {Channel()}";
}
class Sms : Notification
{
public override string Channel() => "sms";
}
Code example: Notifications that know their channel#
class Notification {
constructor(to) {
this.to = to;
}
channel() {
return 'generic';
}
format(message) {
return `To ${this.to}: ${message}`;
}
send(message) {
return `[${this.channel()}] ${this.format(message)}`;
}
}
class Email extends Notification {
channel() { return 'email'; } // replace
}
class Sms extends Notification {
channel() { return 'sms'; }
format(message) { // extend
const text = super.format(message);
return text.length > 30 ? text.slice(0, 27) + '...' : text;
}
}
const list = [new Notification('admin'), new Email('asha@example.com'), new Sms('9876543210')];
for (const n of list) {
console.log(n.send('Your order has shipped today'));
}
class Money {
constructor(amount) { this.amount = amount; }
toString() { return `₹${this.amount}`; } // override Object.prototype.toString
}
console.log(`Total: ${new Money(250)}`);
[generic] To admin: Your order has shipped today [email] To asha@example.com: Your order has shipped today [sms] To 9876543210: Your order h... Total: ₹250
Step-by-step explanation#
- send() is written once in Notification and calls this.channel() and this.format().
- Email replaces channel(); Sms replaces channel() and extends format().
- Sms.format calls super.format() to reuse the parent’s text, then shortens it to fit an SMS.
- The loop calls the same send() on each object, and each object’s overrides run.
- Money overrides toString, so the template string prints ₹250 instead of [object Object].
Real-world use case#
In ASP.NET Core, you override OnActionExecuting in a filter, ToString() in value objects for logging, and SaveChangesAsync in a DbContext to set audit fields before data is saved.
Common mistakes#
An override of save() that deletes instead breaks every caller. Overrides must keep the parent’s promise (Liskov Substitution Principle).
If the parent does important work (validation, logging), skipping super.format() silently drops it.
Without override, the parent version runs when the object is used through a parent-type variable.
Best practices#
- Override to specialise, not to change the meaning
- Call super / base when the parent’s work is still needed
- Override toString / ToString for debugging output
- In C#, mark only intended extension points virtual
Remember this#
Overriding = same signature, child class, chosen at RUNTIME.
super.method() / base.Method() = “do what my parent does, then more”.
Overloading ≠ overriding.
Practice exercise#
Your task: Create a Shape class with describe() returning "A shape", and a Circle subclass whose describe() returns the parent text plus " that is round". Use super.
Need a hint?
Show solution
class Shape { describe() { return 'A shape'; } }
class Circle extends Shape {
describe() { return super.describe() + ' that is round'; }
}
console.log(new Circle().describe()); // A shape that is round
Mini quiz#
What is logged?
class A { who() { return 'A'; } hello() { return 'I am ' + this.who(); } }
class B extends A { who() { return 'B'; } }
console.log(new B().hello());
Want more? Practise all OOP Concepts MCQs · OOP Concepts interview questions
My notes#
Saved in this browser.