Sunday, July 11, 2021

Java Best Practices for Method Overloading? Examples

You need to be careful while overloading a method in Java, especially after the introduction of autoboxing in Java 5. Poorly overloaded method not only adds confusion among developers who use that but also they are error-prone and leaves your program at compiler's mercy to select proper method. One of the best examples of a poorly overloaded method is the removal method of ArrayList. There are two versions of remove, first, one which takes an Object as argument i.e. remove(Object element), and the second one, which takes an index as argument i.e. remove(int index).

It worked fine until Java 1.4 where there is clearly a distinction between primitive types and objects type but in Java 1.5, where you can pass an int primitive to a method which accepts an Integer object, creates some nasty problem.

Now suppose you have an ArrayList of Integer with values 1, 2, and 3, and you call remove(1) then which method will be called? JVM can interpret 1 as index also or 1 as Integer object also.

It's best to avoid issues related to method overloading by following some Java best practices. For those who don’t know what is method overloading in Java? method overloading means declaring more than one method with the same name but different method signatures.

This is generally done to create methods that do the same thing but with different types. For example, one of the most popular examples of method overloading is System.out.println() method, which is overloaded to accept different types of parameters like String, double, int, etc, see this Java tutorial on method overloading and static vs dynamic binding for more details.

By the way, all of these Java best practices which are explained in the context of method overloading are equally applicable to constructor overloading in Java because in terms of overloading methods and constructors are almost the same. You can also join these Java Programming courses to learn more about essential OOP concepts in Java. 





Java Best Practices -  Method Overloading

Java best practices for method and constructor overloading in JavaHere are some of the common things which you can remember while overloading the method or constructor in Java. These Java best practices are completely based upon experience and you may have some more to add to this list. let’s see my list of Java best practices while overloading the method in Java.


1. Don't overload method which accepts the same number of the parameter with similar types

Two overloaded methods which accepts the same number of an argument with similar types i.e. which follow same type hierarchy is the most common mistake while overloading a method in Java. For example, find out which version of the overloaded method will be invoked in the following scenario :

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * Java program to demonstrate some best practices
 * to follow while overloading
 * method in Java. This Java program shows
 * a case of confusing method overloading in Java
 *
 * @author Javin Paul
 */

public class OverloadingTest {
 
    public static void main(String args[]){
       List abc = new ArrayList();
       List bcd = new LinkedList();
     
       ConfusingOverloading co = new ConfusingOverloading();
       co.hasDuplicates(abc); //should call to ArryList overloaded method
       co.hasDuplicates(bcd); //should call to LinkedList overloaded method
    }

 
}

class ConfusingOverloading{
 
    public boolean hasDuplicates (List collection){
        System.out.println("overloaded method with Type List ");
        return true;
    }
 
    public boolean hasDuplicates (ArrayList collection){
        System.out.println("overloaded method with Type ArrayList ");
        return true;
    }
 
 
    public boolean hasDuplicates (LinkedList collection){
        System.out.println("overloaded method with Type LinkedList ");
        return true;
    }
 
}

Output
the overloaded method with Type List
the overloaded method with Type List

To the surprise of some programmers method with argument type List is called both the time, instead of expected method which takes ArrayList and LinkedList, because method overloading is resolved at compile time using static binding in Java.

This is also one of the reasons, why it's important to clearly understand the difference between method overloading and overriding in Java. Here expected case is the result of mistaking overloading as overriding, which works on the actual object and happens at runtime. To know more about static and dynamic binding in Java, you can also see my post difference between static and dynamic binding in Java.



2. Use radically different types while overloading method in Java

It's completely legal and there is no ambiguity when two overloaded methods accept radically different types like String and Integer. Though both overloaded methods will accept only one parameter, it’s still clear which method is called because both types are completely different to each other.

Both programmer and compiler both know which method will be invoked for a particular call. One of the examples of this kind of overloading is the constructor of java.util.Scanner class which accepts File, InputStream or String as a parameter, as shown below :

Scanner(File source)
Scanner(InputStream source)
Scanner(String source)

Method Overloading Best Practices in Java



3. Beware of Autoboxing while overloading method in Java

Prior to the introduction of Autoboxing and unboxing in Java 5, the method which accepts primitive type and object type was radically different and it’s clear which method will be invoked. Now with autoboxing, it's really confusing.

A classical example of this kind of overloading mistake is ArrayList’s remove() method, which is overloaded to accept index as well as Object. when you store Integer in ArrayList and call the remove() method, It’s hard to find out which remove() method will be called, as shown in the below example :

List<Integer> numbers = new ArrayList<Integer>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
System.out.println("numbers: " + numbers);
numbers.remove(1); //should remove "1" as element or 2nd element from ArrayList
System.out.println("numbers: " + numbers);

Output:
numbers: [1, 2, 3]
numbers: [1, 3]

Many Java programmer expects that Integer(1) object would be removed but since remove() is overloaded, the compiler chooses to remove(int) over remove(Object). Rules of which overloaded method gets chosen in case of autoboxing is complex and hard to remember, so It's best to avoid two overloaded methods where one accepts Object and the other accept primitive type. If by any chance you must have to do this then make sure both of them perform the identical function.


Other Java best practices articles from Javarevisited Blog

5 comments :

Steve said...

I like this kind of advice which is based on experience. Though You don't call all these as Java best practices, rather common mistakes to avoid while doing method overloading and constructor overloading in Java.

James said...

One more to add in your list of best practices. Suppose If you like to overload methods so that it will work for every type in Java than you need to overload it for primitive type
boolean
byte
char
short
int
float
double
and Object.

All most all API including JDK does that. Look at Arrays.toString() method it has overloaded for all above types.

Unknown said...

thanks dude, for giving such a great information. but i want to know, why method overloading called compile time polymorphism?

Arun Singh said...

really helpful article, sir.
THanks

javin paul said...

Thank you Arun, I am glad that you find these best practices about method overloading and constructor useful.

Post a Comment