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 the overriding of methods (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. It’s a classic for explaining inheritance in OOP: an example with Dogs and Cats.
Suppose we have a base class Animal. This class has an instance method called MakeSound() that prints to the console “The animal makes a sound”.
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 word (like override or new) and others will not (that doesn’t matter for now, we’ll see it later with examples in specific languages).
Now the important thing is to understand how Inheritance and Polymorphism work with this method we have overridden.
Calling Overridden Methods
Let’s look at the two simplest cases:
- Instance of
Animalstored in anAnimalvariable - Instance of
Dogstored in aDogvariable
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, indeed, the derived class Dog has overridden the method from its base class Animal, and when we call it, it invokes its own 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 one 👇
Animal animalDog = new Dog();
animalDog.MakeSound(); // Output: The dog barks
In general, in most languages (each with its own detail), it is normal for the method of the derived class to be invoked. That is, the type of variable doesn’t matter; what matters is the instance stored in it.
Since 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 method overriding and Polymorphism in action.
Some languages may have their particularities, such as requiring the overridden method to be marked with a reserved word (like override or new). But, in principle, the “normal” operation of method override in OOP is what we have just seen.
Base Keyword
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 on screen
- “The animal makes a sound”, when invoking the
base.MakeSound()method - “The dog barks”, when invoking its own code
Variable Override
Variable override, 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 from 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
Childclass declares a variableMessage - This hides the
Messagevariable from theParentclass - When accessing the
Messagevariable from an instance of theChildclass - Therefore, the value Hello from the Child class is displayed
It is not as common as method override. In fact, it’s not particularly useful. But if you find a language that allows it, well, there it is.
Examples in Different Languages
Now let’s see an example of how method overriding is done in different programming languages.
In C#, method overriding is done using the override keyword in the derived class to override the behavior of the method 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 override keyword 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 perform the redefinition of methods from the base class 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 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 done simply 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
