We have already seen that in object-oriented programming (OOP), one of the most important and fundamental concepts is the ability of Inheritance and Polymorphism among others.
Both concepts rely heavily on method overriding (or override in English) of methods and variables in derived classes.
Method overriding is a feature that allows a derived class to provide a different implementation of a method that is already defined in its base class.
Overriding is used to modify the behavior of inherited methods, allowing subclasses to customize or replace the behavior of methods defined in base classes.
Practical Case
Let’s look at an example to understand how methods are overridden. A classic example is used to explain inheritance in OOP, an example with Puppies and Kittens.
Suppose we have a base class Animal
. This class has an instance method called MakeSound()
that prints “The animal makes a sound” to the console.
Now we take a derived class Dog
, which inherits from Animal
, and provide an alternative version of the MakeSound()
method.
class Animal
{
// Method in the base class
MakeSound()
{
Console.Log("The animal makes a sound");
}
}
class Dog extends Animal
{
// Overriding the method in the child class
override MakeSound()
{
Console.Log("The dog barks");
}
}
Some languages will require marking the overridden method with a reserved keyword (like override
or new
) and others will not (that doesn’t matter to me now, we will see it at the end with examples in specific languages).
Now the important thing is to understand how Inheritance and Polymorphism work with this overridden method.
Calling Overridden Methods
Let’s look at the two simplest cases:
- Instance of
Animal
stored in variableAnimal
- Instance of
Dog
stored in variableDog
Animal myAnimal = new Animal();
myAnimal.MakeSound(); // Output: The animal makes a sound
Dog myDog = new Dog();
myDog.MakeSound(); // Output: The dog barks
As we can see, when invoking MakeSound()
, each instance calls its own definition of MakeSound()
. Logical and expected.
Here we see that the derived class Dog
has indeed overridden the method of its base class Animal
, and when we call it, it invokes its method (not the one from its base class).
Now, the interesting case is what happens when we store an instance of Dog
in a variable of type Animal
. That is, the interesting case is this 👇
Animal animalDog = new Dog();
animalDog.MakeSound(); // Output: The dog barks
In general, in most languages (each with its details), the normal behavior is that the derived class’s method is invoked. That is, the variable type doesn’t matter; what matters is the instance we hold in it.
As the instance has the MakeSound()
method overridden, the method that will be called is the one that exists in the Dog
class. Here we have the functioning of method overriding and Polymorphism.
Some languages may have their peculiarities, such as requiring marking the overridden method with a reserved keyword (like override
or new
). But, in principle, the “normal” functioning of method overriding in OOP is what we have just seen.
Keyword base
In some cases, it may be useful to invoke the method of the base class from the derived class using the base
keyword.
Dog extends Animal
{
MakeSound()
{
// Call to the base class method
base.MakeSound();
Console.Log("The dog barks");
// Output: The animal makes a sound
// The dog barks
}
}
In the previous example, MakeSound
in the Dog
class first calls the implementation of the method in the base class Animal
before adding its own additional behavior.
In this case, it would display both messages:
- “The animal makes a sound”, when invoking the method
base.MakeSound()
- “The dog barks”, when invoking its own code
Variable Override
Variable overriding, also known as variable hiding, allows a derived class to declare a variable with the same name as a variable in its base class (that’s why we say it “hides” the variable of the parent class in the child class).
This means that the variable in the child class hides the variable in the parent class and can have a different value.
class Parent
{
string Message = "Hello from the Parent class";
}
class Child extends Parent
{
new string Message = "Hello from the Child class";
}
In this example,
- The
Child
class declares a variableMessage
- This hides the
Message
variable from theParent
class - When accessing the
Message
variable from an instance of theChild
class - Therefore, it shows the value Hello from the Child class
It is not as common as method overriding. In fact, it is not particularly useful. But if you find a language that allows it, well, there it is.
Examples in Different Languages
Now let’s look at an example of how method overriding is done in different programming languages.
In C#, method overriding is done using the keyword override
in the derived class to override the behavior of the function defined in the base class.
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof");
}
}
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine("Meow");
}
}
// Usage
Animal dog = new Dog();
Animal cat = new Cat();
dog.MakeSound(); // Output: Woof
cat.MakeSound(); // Output: Meow
In C++, method overriding is done using the keyword override
in the derived class to indicate that a virtual function from the base class is being overridden.
#include <iostream>
class Animal {
public:
virtual void MakeSound() const {
std::cout << "The animal makes a sound" << std::endl;
}
};
class Dog : public Animal {
public:
void MakeSound() const override {
std::cout << "Woof" << std::endl;
}
};
class Cat : public Animal {
public:
void MakeSound() const override {
std::cout << "Meow" << std::endl;
}
};
// Usage
int main() {
Animal* dog = new Dog();
Animal* cat = new Cat();
dog->MakeSound(); // Output: Woof
cat->MakeSound(); // Output: Meow
delete dog;
delete cat;
return 0;
}
In JavaScript, we can redefine base class methods by declaring the same method in the derived class.
class Animal {
makeSound() {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
makeSound() {
console.log("Woof");
}
}
class Cat extends Animal {
makeSound() {
console.log("Meow");
}
}
// Usage
let animal1 = new Dog();
let animal2 = new Cat();
animal1.makeSound(); // Output: Woof
animal2.makeSound(); // Output: Meow
In TypeScript, the concept of classes and inheritance is similar to that of JavaScript but with the advantage of static typing.
class Animal {
makeSound(): void {
console.log("The animal makes a sound");
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof");
}
}
class Cat extends Animal {
makeSound(): void {
console.log("Meow");
}
}
// Usage
const dog: Animal = new Dog();
const cat: Animal = new Cat();
dog.makeSound(); // Output: Woof
cat.makeSound(); // Output: Meow
In Python, method overriding is simply done by redefining the method in the derived class.
class Animal:
def make_sound(self):
print("The animal makes a sound")
class Dog(Animal):
def make_sound(self):
print("Woof")
class Cat(Animal):
def make_sound(self):
print("Meow")
# Usage
dog = Dog()
cat = Cat()
dog.make_sound() # Output: Woof
cat.make_sound() # Output: Meow