Search for Hubs, Articles and Topics
Search for Hubs, Articles and Topics

Overloading vs Overriding in Java

Learn about Overloading vs Overriding in Java

18 Sep 2021-11 mins read

Introduction

Java is often considered as one of the best languages when we talk about object-oriented programming because of its various concepts and features like classes, objects, inheritance, etc. Polymorphism is one of the core and important concepts of the object-oriented programming paradigm that is supported by Java.

The word polymorphism simply means having many forms. In computer science, it can be inferred to the concept that different types of objects can be accessed through the same interface. Each of the different types can give its independent implementation of the said interface.

The main advantage of polymorphism is that it allows us to simplify the programming interface. We can define conventions that we can then reuse in class after class. For example, the List interface in the Java collection framework defines methods that are used by different implementation classes like ArrayList to define the functionality.

With polymorphism, we can write methods that can process a variety of different types of functionalities with the same name. This also helps us to achieve consistency in our code.

In Java, we have majorly two types of polymorphism as shown below. We’ll cover their details in later sections.

two types of polymorphism in java

Difference Between: Overloading vs Overriding in Java

BasisMethod OverloadingMethod Overriding
DefinitionMethod overloading is when two or more methods have the same name and different argumentsMethod overriding is when a subclass modifies a method of superclass having the same signature
Method SignatureThe overloaded methods must have different method signatures. It means that the methods must differ in at least one of these: number of parameters, type of parameters, and order of parameters, but should have the same nameThe overridden methods must have the same method signature. It means that method name, number of arguments, order of arguments, and type of arguments should be an exact match
Return TypeThe return type of overloaded methods may or may not have the same return typeIf the base class method has a primitive data type, then the overridden method of the subclass must have the same data type.But if the base class method has a derived return type, then the overridden method of the subclass must have either the same data type or subclass of the derived data type.
Access ModifierWe do not have any restriction on access modifiers and may use any access modifier in the overloaded methodThe overridden method in subclass should have the same or less restricted access modifier
BindingThe method call is binded to method definition at compile time. Also known as Static Binding.The method call is binded to method definition at compile time. Also known as Dynamic Binding.
Polymorphism TypeIt is an application of compile-time polymorphismIt is an application of run-time polymorphism
ScopeThe overloading functions always exist in the same scopeThe overriding function always exists in different scope
PurposeGives functionality to allow different methods to have the same name and act differently based on the parameters supplied to themGives functionality to add some extra or unexpected functionality in the subclass as compared to the superclass
Inheritance ScopeDoesn't need inheritance, can be performed within the same classRelies on inheritance, occurs in classes depicting is-a (parent-child) relationship
Private MethodsCan be overloadedCannot be overridden
Final MethodsCan be overloadedCannot be overridden
Static MethodsCan be overloadedCannot be overridden
Error HandlingIf overloading breaks, we get a compile-time error which is relatively easy to fixIf overriding breaks, we get run-time errors which are more serious and relatively more complex to fix.
UsageWe can overload methods any number of times in a classWe can override a method only one time in an inheriting class

What is Method Overloading?

Method overloading in Java refers to the concept where more than one method shares the same method name and has a different parameters list in the same class. With the use of method overloading, we can define methods that do similar tasks under the same name.

Method overloading is an example of compile-time polymorphism, static or early binding. Compile-time polymorphism is when we bind an object with its functionality at compile-time only. In Java, the JVM knows before executing the code which method or object it is going to execute for a particular call.

Rules of Method Overloading

To overload methods in Java, we can change one or more of the following:

  • The number of parameters
  • The type of parameters
  • The order of parameters

Why Method Overloading?

Let’s take an example to understand why we need method overloading. Suppose, we have a problem where we want to perform addition on different types (float, int, double, etc) and different counts of numbers. When trying to solve this problem in Java, we make a class Addition that will have different methods for different types of addition. Without method overloading, we’d have to create methods with different names like addTwoIntegers(), addIntegerAndFloat(), etc. as shown in the diagram below:

why method overloading

Let’s see how method overloading can help us to solve the above problem by overloading a simple add() method:

overloading a simple add method

In the above diagram, we are overloading the add() method by passing different type, order, and number of arguments. We’ll see the full code of this example in the next section.

In method overloading, the return type can or cannot be the same, but we must change the parameters list because, in Java, we cannot achieve method overloading by changing only the return type of the method. The reason behind this is that it causes ambiguity as, for two methods having the same parameters list, the JVM cannot decide which method to call as we cannot know the return type before execution of the method.

The following rules must be obeyed while method overloading:

  • Return types cannot be overloaded
  • We cannot overload two methods by just a static keyword, we can only overload them if their parameters list is different
  • We can overload Java’s main() method but JVM will call the main method having an array of string arguments

Example of Method Overloading

Let’s try to solve the above problem with the help of a code example of method overloading in Java. We will implement the above solution of overloading the add() method of the Addition class. This class will perform addition according to the type, number, and order of arguments it receives.

class Addition {
    public int add(int integer1, int integer2) {
        return integer1 + integer2;
    }
 
    public float add(int integer1, float float1) {
        return integer1 + float1;
    }
 
    public int add(int integer1, int integer2, int integer3) {
        return integer1 + integer2 + integer3;
    }
 
    public float add(float float1, int integer1) {
        return float1 + integer1;
    }
}
public class OverloadingExample {
    public static void main(String[] args) { 
        Addition addition = new Addition();
 
        //will call add(int integer1, int integer2)
        System.out.println(addition.add(1, 2));
        
        //will call add(int integer1, float float1)
        System.out.println(addition.add(1, 2.3F));
     
        //will call add(int integer1, int integer2, int integer3)
        System.out.println(addition.add(1, 2, 3));
        
        //will call add(float float1, int integer1)
        System.out.println(addition.add(1.5f, 2));
    }
}

If we run the above code, we’ll get the following output:

3
3.3
6
3.5

Now, as we can see, we have called add() with different combinations of parameters, and each time the JVM automatically calls the required method. This is how we can do method overloading.

Also if we are using an IDE and do “ctrl+click” on any of add() methods in the main() function, we’ll see that it takes us to the function it is going to call while execution. This shows that before running our code, JVM already knows which method to execute for a call. This is why it is also called compile-time polymorphism.

What is Method Overriding?

Method overriding is simply redefining a function of a superclass in the subclass with the same method signature. The same signature means the methods must have the same name, the same number of parameters, and the same type of parameters in the same sequence. It is used to change the behavior of existing methods, to provide fine-grained implementations in subclasses for methods defined in a superclass.

Rules of Method Overriding

The following rules must be obeyed while method overriding:

  • Method overriding can only be implemented when two classes have an ‘is-a’ relationship of inheritance between them.
  • The return type of the overridden method must be the same or covariant of the return type of the superclass method. For example, in the following code snippet, we can see that the return type of overriding method countIntegers() is Integer which is a subtype of the return type of overridden method countNumbers() that is Number.
class NumbersBucket {
   ...
   public Number countNumbers() {
       ....
   }
}
class IntegersBucket extends NumbersBucket {
    ...
    @Override
    public Integer countIntegers() {
        ....
    }
}

  • It can’t have a more restrictive access modifier. For example, if the parent’s method is protected, then the child’s overridden method cannot be private.
  • @Override annotation may or may not be used on the overridden methods, but using this annotation can help us identify if we are not obeying any overriding rules as we get a compile-time error if we are not properly overriding the method annotated with @Override annotation. But in case the method is not annotated with @Override annotation, then if the method is not obeying overriding rules, the JVM treats the method as a new method.
  • Only inherited methods can be overridden in the subclass. Constructors and private methods cannot be overridden as they’re not inherited. We must override the abstract methods in the first concrete i.e non-abstract subclass. Final and static methods cannot be overridden, only instance methods are overridden.
  • We can also call the superclass method in the subclass using the super keyword, to get the superclass version of an overridden method.

Method overriding is an example of run-time polymorphism, dynamic or late binding. It is also known as Dynamic Method Dispatch because the method that is going to be called is decided at run time by the JVM.

Why Method Overriding?

Let’s take an example to understand why we need method overriding.

Let’s say we want to model a scenario where we had a class Train having three basic methods, start(), stop(), and accelerate(). Now, we have a situation where we want to have another class BulletTrain that does everything that the Train class does but accelerates with a higher speed.

As we know, the best thing for us to do in this case is to inherit the BulletTrain class from the Train class. Now, if there was no method overriding, we would have to create a different method that would provide the implementation for faster acceleration in the BulletTrain class, say accelerateFast(). This is where method overriding comes into the picture and helps us change the implementation of a method from the base class in the subclass.

Let’s see how overriding methods helps us solve the above problem with the help of the following diagram:

overriding methods to solve problems

In the diagram above, we’ve simply overridden the accelerate() method of the Train class to provide a more refined implementation for the subclass BulletTrain. We’ll see the full code of this example in the next section.

Example of Method Overriding

Let’s try to solve the above problem with the help of a code example of method overriding in Java. We’ll override the accelerate() method in the BulletTrain subclass to give a more refined implementation.

public class Train {
   public String accelerate(int mph) {
       return "The train accelerates at " + mph + " MPH.";
   }
   public String start() {
       return "The train is running.";
   }
   public String stop() {
       return "The train has stopped.";
   }
}
public class BulletTrain extends Train {
   @Override
   public String accelerate(long mph) {
       return "The bullet train accelerates at " + mph + " MPH.";
   }
   @Override
   public String start() {
       return "The bullet train is running.";
   }
   @Override
   public String stop() {
       return "The bullet train has stopped.";
   }
}
public class MethodOverridingImpl {
   public static void main(String[] args) {
       Train train = new Train();
       String runTrain = train.start();
       String accelerateTrain = train.accelerate(70);
       String stopTrain = train.stop();
       System.out.println(runTrain);
       System.out.println(accelerateTrain );
       System.out.println(stopTrain)
       Train bulletTrain = new BulletTrain();
       String runBulletTrain = bulletTrain.start();
       String accelerateBulletTrain = bulletTrain.accelerate(310);
       String stopBulletTrain = bulletTrain.stop();
       System.out.println(runBulletTrain);
       System.out.println(accelerateBulletTrain);
       System.out.println(stopBulletTrain)
   }
}

If we run the above code, we’ll get the following output:

The train is running.
The train accelerates at 70 MPH.
The train has stopped.
The bullet train is running.
The bullet train accelerates at 310 MPH.
The bullet train has stopped.

In the above code example, it’s clear to see that if an application uses instances of the Train class, then it can work with instances of BulletTrain as well, as both implementations of the accelerate() method have the same signature and the same return type.

Which accelerate() method will be executed depends on the object that is calling the method. If the object of the BulletTrain class calls the accelerate(), the method of the BulletTrain class will override the Train class method and the same method will be executed. Otherwise, if the object of the Train class calls the method, the Train class method will be executed.

Conclusion

In this article, we discussed two of the very useful applications of Polymorphism in Java which are Method Overloading and Method Overriding. It is to be noted that although they are very powerful and useful, we should use them wisely, otherwise, their whole purpose could be defeated. Method overloading should be used where we need multiple methods that are doing the same thing with different parameters. Method overriding should be used when the subclass needs to alter or modify the behavior of the parent class method.