Method Override vs Overload in Java

Overview

1. Introduction

In this tutorial, we'll examine the differences between method override and overload and the forms to implement them in Java.

2.The Multiple Method Name Problem

A method signature in Java is composed of four mandatory parts:

The modifier accessor, for instance, public, private, or protected. The return type. The method name. A sequence of parameters.

For example, we can design a public method that takes two numbers and return their sum as follows:

1public class Calculator {
2  
3  public int addNumbers(int a, int b) { 
4    return a + b; 
5  }
6}

But, if we want to add parameters of types double or float? We cannot specify another method in Java with the name addNumbers in the same class (without overloading). So, one option here would be to create another method with a different name:

 1public class Calculator {
 2
 3  public int addNumbers(int a, int b) { 
 4    return a + b; 
 5  }
 6
 7  public double addDoubleNumbers(double a, double b) { 
 8    return a + b;
 9  }
10}

That could be more practical in terms of maintainability. It would be so much easier to have the same method name apply to various argument types. For instance, the same addNumbers should be able to add doubles, floats, ints, or any number. Java solves that issue using method overloading.

3. Method Overloading

Overloading a method refers to the ability to specify any number of methods with the same name as long as the return type or its parameters are different.

Let's overload our original addNumbers method to accept different types of arguments:

 1public class Calculator {
 2
 3  public int addNumbers(int a, int b) { 
 4    return a + b; 
 5  }
 6
 7  public double addNumbers(double a, double b) { 
 8    return a + b; 
 9  }
10}

Overloading is especially useful for defining methods with the same name that accept different parameters and return types. Imagine our Calculator without method overloading. It would have a unique method name for every number type. That can lead to difficulty for developers to remember all method names, thus creating a more complex API.

4. Method Overriding

When we define sub-classes of a class or an implementation of an interface, all fields and methods are inherited. The thing with inherited methods is that we sometimes want different superclass behavior—Java solves that issue with the method overriding feature.

Override a method refers to extending the behavior from the superclass and creating a different implementation. By doing that, we drive distinct behaviors of the same class method in its subclasses.

4.1. Concrete and Abstract Class Methods Overriding

Override a concrete method is optional, whereas overriding abstract methods is mandatory. That's because abstract methods don't have any implementation initially. Thus, we must create one in the subclasses that inherit that method.

To exemplify method overriding, let's rewrite the Calculator class to be an abstract class with two versions, one concrete and one abstract, of the addNumbers method:

1public abstract class Calculator {
2
3  public int addNumbers(int a, int b) { 
4    return a + b; 
5  }
6
7  public abstract double addNumbers(double a, double b);
8}

Let's also define the AlienCalculator as a subclass of Calculator. The integer version of addNumbers uses the default implementation of the Calculator class. We should override the double version of addNumbers in Calculator since it's abstract.

Assuming that aliens calculate the sum of two doubles by adding them together and squaring the final result, the AlienCalculator class would look like this:

1public class AlienCalculator extends Calculator {
2
3  @Override
4  public double addNumbers(double a, double b) {
5    return Math.pow(a + b, 2);
6  }
7}

Here's what's going on in the code above:

  • We didn't override the integer version of addNumbers. In that case, Java assumes we want the superclass's default implementation.
  • The double version of addNumbers is an overridden version that calculates the sum using a specific logic defined by the AlienCalculator. The @Override annotation is optional, but it is helpful to spot a method override quickly.

Let's verify the results of summing up two numbers using AlienCalculator with a unit test:

 1public class CalculatorTests {
 2
 3  AlienCalculator alienCalculator = new AlienCalculator();
 4
 5  @Test
 6  public void givenTwoNumbers_whenCalculateInAlienMathematics_returnCorrectResult() {
 7    int intResult = alienCalculator.addNumbers(2, 5);
 8    double doubleResult = alienCalculator.addNumbers(2.0d, 5.0d);
 9
10    assertEquals(49.0d, doubleResult);
11    assertEquals(7, intResult);
12  }
13}

4.2. Interface Methods Overriding

An interface is a definition of an API that contains various abstract methods without any implementation. Therefore, Java obligates us to override all abstract methods from an interface with a custom implementation. Let's rewrite the _Calculator class to be an interface with two abstract methods:

1public interface Calculator {
2
3  public int addNumbers(int a, int b);
4
5  public double addNumbers(double a, double b);
6}

The AlienCalculator is an implementation of that class:

 1public class AlienCalculator implements Calculator {
 2
 3  @Override
 4  public int addNumbers(int a, int b) {
 5    return a + b;
 6  }
 7
 8  @Override
 9  public double addNumbers(double a, double b) {
10    return Math.pow(a + b, 2);
11  }
12}

Now, the AlienCalculator class should implement both versions of the addNumbers method because both are abstract. To do that, we use the implements keyword and rewrite both methods with custom logic.

5. Conclusion

In this post, we've seen how to override and overload a method from concrete classes, abstract classes, and interfaces.