What is Method Overriding?
Method Overriding occurs when a subclass redefines a method that already exists in its parent class, providing a specialized or completely different implementation. The overriding method has the same name, same parameters, and same return type. It is the core mechanism of runtime polymorphism — which method runs is determined at execution time based on the actual object type.
Explanation
Real-World Analogy
- A company 🏢 has a standard
generate_report()procedure. TheSalesTeamoverrides it to generate a revenue report, and theHRTeamoverrides it to generate a headcount report. Same method name, called the same way — but each department’s version does something different. - Same call:
team.generate_report()→ different output depending on the actual team type.
Overriding vs Overloading
| Feature | Overriding | Method Overloading |
|---|---|---|
| Location | Different classes (parent/child) | Same class |
| Parameters | Must match parent | Must differ |
| Return type | Usually same | Can differ |
| When resolved | Runtime (dynamic dispatch) | Compile time (static) |
| Polymorphism | Runtime / Dynamic | Compile-time / Static |
The super() Role in Overriding
- When overriding, you often want to extend the parent’s behavior rather than completely replace it. Use
super()to call the parent’s version first:
class Animal:
def speak(self):
return "..."
class Dog(Animal):
def speak(self): # Override
parent_sound = super().speak() # extend parent
return f"Woof! (also: {parent_sound})"Rules for Valid Overriding
| Rule | Detail |
|---|---|
| Same name | Method name must be identical |
| Same parameters | Signature must match (Java/C# strict; Python flexible) |
| Same or wider return type | Can return a subtype (covariant return — Java/C++) |
| Same or broader access | Can’t narrow access (public → private is invalid) |
| Not final/static | Final methods can’t be overridden; static methods are hidden, not overridden |
Implementation
-
A
Shape → Circle / Rectangle / Trianglehierarchy demonstrating full override ofarea(),perimeter(), anddescribe(). Languages: Python · Cpp · Java · Java Script · CSharp
# ─── Python ──────────────────────────────────────────────────────────
import math
from abc import ABC, abstractmethod
class Shape(ABC):
def __init__(self, color: str = "white"):
self.color = color
@abstractmethod
def area(self) -> float: ...
@abstractmethod
def perimeter(self) -> float: ...
# Non-abstract method — can be overridden or inherited
def describe(self) -> str:
return (f"{self.__class__.__name__}[{self.color}]: "
f"area={self.area():.2f}, perimeter={self.perimeter():.2f}")
class Circle(Shape):
def __init__(self, radius: float, color: str = "red"):
super().__init__(color) # call parent __init__
self.radius = radius
def area(self) -> float: # OVERRIDE abstract
return math.pi * self.radius ** 2
def perimeter(self) -> float: # OVERRIDE abstract
return 2 * math.pi * self.radius
def describe(self) -> str: # OVERRIDE non-abstract (extend)
base = super().describe() # call parent's describe()
return f"{base} | radius={self.radius}"
class Square(Shape):
def __init__(self, side: float, color: str = "blue"):
super().__init__(color)
self.side = side
def area(self) -> float:
return self.side ** 2
def perimeter(self) -> float:
return 4 * self.side
# describe() is NOT overridden — inherits parent version
# Polymorphism: same call, different behavior
shapes: list[Shape] = [Circle(5), Square(4), Circle(3, "green")]
for shape in shapes:
print(shape.describe())
# Circle[red]: area=78.54, perimeter=31.42 | radius=5
# Square[blue]: area=16.00, perimeter=16.00
# Circle[green]: area=28.27, perimeter=18.85 | radius=3
# Works via base type reference — runtime dispatch
def print_shape(s: Shape) -> None:
print(f"Type: {type(s).__name__}, Area: {s.area():.2f}")
for shape in shapes:
print_shape(shape)// ─── C++ — virtual + override ─────────────────────────────────────────
#include <iostream>
#include <cmath>
#include <vector>
#include <memory>
class Shape {
protected:
std::string color_;
public:
Shape(std::string color = "white") : color_(color) {}
virtual double area() const = 0; // pure virtual — MUST override
virtual double perimeter() const = 0;
virtual std::string describe() const { // virtual — CAN override
return color_ + " shape: area=" + std::to_string(area());
}
virtual ~Shape() {}
};
class Circle : public Shape {
double radius_;
public:
Circle(double r, std::string color = "red")
: Shape(color), radius_(r) {}
double area() const override {
return M_PI * radius_ * radius_;
}
double perimeter() const override {
return 2 * M_PI * radius_;
}
std::string describe() const override {
return Shape::describe() + " | Circle r=" + std::to_string(radius_);
}
};
class Square : public Shape {
double side_;
public:
Square(double s, std::string color = "blue") : Shape(color), side_(s) {}
double area() const override { return side_ * side_; }
double perimeter() const override { return 4 * side_; }
// describe() not overridden — uses Shape::describe()
};
int main() {
std::vector<std::unique_ptr<Shape>> shapes;
shapes.push_back(std::make_unique<Circle>(5));
shapes.push_back(std::make_unique<Square>(4));
for (const auto& s : shapes) {
std::cout << s->describe() << "\n"; // virtual dispatch
std::cout << "Area: " << s->area() << "\n";
}
}// ─── Java — @Override annotation ─────────────────────────────────────
import java.util.*;
abstract class Shape {
protected String color;
Shape(String color) { this.color = color; }
public abstract double area();
public abstract double perimeter();
// Non-abstract — can be overridden
public String describe() {
return String.format("%s[%s]: area=%.2f, perim=%.2f",
getClass().getSimpleName(), color, area(), perimeter());
}
}
class Circle extends Shape {
double radius;
Circle(double r, String color) { super(color); this.radius = r; }
@Override public double area() { return Math.PI * radius * radius; }
@Override public double perimeter() { return 2 * Math.PI * radius; }
@Override public String describe() { // extends parent
return super.describe() + " | radius=" + radius;
}
}
class Square extends Shape {
double side;
Square(double s, String color) { super(color); this.side = s; }
@Override public double area() { return side * side; }
@Override public double perimeter() { return 4 * side; }
// describe() inherited from Shape — not overridden
}
class OverrideDemo {
static void printShape(Shape s) {
System.out.println(s.describe());
}
public static void main(String[] args) {
List<Shape> shapes = List.of(
new Circle(5, "red"),
new Square(4, "blue")
);
shapes.forEach(OverrideDemo::printShape);
}
}// ─── JavaScript ───────────────────────────────────────────────────────
class Shape {
constructor(color = "white") { this.color = color; }
area() { throw new Error("area() must be overridden"); }
perimeter() { throw new Error("perimeter() must be overridden"); }
describe() {
return `${this.constructor.name}[${this.color}]: area=${this.area().toFixed(2)}`;
}
}
class Circle extends Shape {
constructor(radius, color = "red") { super(color); this.radius = radius; }
area() { return Math.PI * this.radius ** 2; } // Override
perimeter() { return 2 * Math.PI * this.radius; } // Override
describe() { // Override + extend
return `${super.describe()} | radius=${this.radius}`;
}
}
class Square extends Shape {
constructor(side, color = "blue") { super(color); this.side = side; }
area() { return this.side ** 2; }
perimeter() { return 4 * this.side; }
// describe() inherited — not overridden
}
const shapes = [new Circle(5), new Square(4)];
shapes.forEach(s => console.log(s.describe()));// ─── C# — virtual + override ─────────────────────────────────────────
using System;
using System.Collections.Generic;
abstract class Shape {
protected string Color;
protected Shape(string color = "white") { Color = color; }
public abstract double Area();
public abstract double Perimeter();
public virtual string Describe() =>
$"{GetType().Name}[{Color}]: area={Area():F2}, perim={Perimeter():F2}";
}
class Circle : Shape {
double radius;
public Circle(double r, string color = "red") : base(color) { radius = r; }
public override double Area() => Math.PI * radius * radius;
public override double Perimeter() => 2 * Math.PI * radius;
public override string Describe() => // extend base
$"{base.Describe()} | radius={radius}";
}
class Square : Shape {
double side;
public Square(double s, string color = "blue") : base(color) { side = s; }
public override double Area() => side * side;
public override double Perimeter() => 4 * side;
// Describe() not overridden — uses Shape.Describe()
}
class Program {
static void Main() {
var shapes = new List<Shape> { new Circle(5), new Square(4) };
foreach (var s in shapes) Console.WriteLine(s.Describe());
}
}
Key Takeaways
- Same signature, different class — that’s method overriding.
- Resolved at runtime — the actual object’s class determines which version runs.
- Use
super()/super.method()/base.Method()to extend (not replace) parent behavior. @Override(Java) /override(C#/C++) keywords catch typos at compile time — always use them.final(Java) /sealed(C#) / novirtual(C++) methods cannot be overridden.