Method Overloading Rules in Java: Widening, Autoboxing and Var-args

Method Overloading in Java: Overview

In Java or any other programming language that supports method overloading, a method is said to be overloaded if two or more methods in a class or subclass have same name but different parameter declarations. The overloaded method is a completely different method from any other method of the same name. In the simplest words, overloaded methods let you reuse the same method name in a class. Method overloading is one of the ways that Java supports polymorphism.

Method overloading is programmer friendly polymorphic feature, where a programmer needs to remember only one method name for similar type of operations on different types and number of parameters. The compiler takes care of calling the right function by looking at what type and number of parameters are passed.

Java Method Overloading Rules

When an overloaded method is invoked, Java uses the type and/or number of arguments to determine which version of the overloaded method to actually call. Thus, overloaded methods must differ in the type and/or number of their parameters. While overloaded methods may have different return types, the return type alone is insufficient to distinguish two versions of a method. When Java encounters a call to an overloaded method, it simply executes the version of the method whose parameters match the arguments used in the call. The rules are as follows:

  • Overloaded methods MUST differentiate argument list either number or type of arguments.
  • Return type of overloaded methods CAN be same or different. It plays no role in overloading
  • Access specifier (public, protected, or private) CAN also be same or different for overloaded methods.
  • Overloaded methods CAN declare new or broader checked exceptions.
  • Two methods with the same name but in different classes can still be considered overloaded, if one class inherits the other and the subclass inherits one version of the method and then declares another overloaded version in its class definition.

Legal Method Overloading Examples in Java

Let's take a look at the following method signatures, where we want to overload the method drawTriangle.

public void drawTriangle(int base) 
{ // draws equilateral triangle }

The following methods are legal overloads of the drawTriangle method:

public void drawTriangle(int base, int height) 
{ // computes hypotenuse and draws right angle triangle }

public void drawTriangle(int base, int height, int hypotenuse) 
{ // check if triangle can be drawn, draws triangle }

If you look at the following overloaded method signatures they seem quite simple but this is not always the case. Sometimes, overloaded methods may be confusing, especially when overriding and overloading happens together. For an example, you have to be careful recognizing if a method is overloaded or overridden. You might see a method that appears to be violating a rule for overriding, but that is actually a legal overload, as follows:

class A
{
  public void aMethod (int x)
  {
    System.out.println("x: " + x);
  }
}
 
class B extends A
{
  // overridden method
  public void aMethod (int x)
  {
    System.out.println("subclass x: " + x);
  }
 
  // following method looks like 
  // overridden but it is overloaded
  public void aMethod (String s)
  {
    System.out.println("s: " + s);
  }
}

In above example, public void aMethod (String s) looks like an overridden method but subclass B overloads the aMethod method, by changing the argument type.

Choosing Overloaded Methods When Pass Literal Parameters

Imagine, you have two overloaded method signatures as aMethod(byte x) and aMethod(int x). In this case which method definition will be called if you make a call by passing a fixed value (literal) 10 to method aMethod as aMethod(10)? The answer is aMethod(int x) because Java by default treats integer literals int and floating point literals double. See the following example code and observe its output:

class B
{
  public void aMethod (byte x)
  {
    System.out.println("byte x: " + x);
  }
 
  public void aMethod (short x)
  {
    System.out.println("short x: " + x);
  }
 
  public void aMethod (int x)
  {
    System.out.println("int x: " + x);
  }
 
  public void aMethod (long x)
  {
    System.out.println("long x: " + x);
  }
 
  public void aMethod (float x)
  {
    System.out.println("float x: " + x);
  }
 
  public void aMethod (double x)
  {
    System.out.println("double x: " + x);
  }  
}
 
class PracticeExercise1
{
  public static void main (String[] args)
  {
    B bb = new B();
    bb.aMethod((long)(10)); //long version will be called
    bb.aMethod(10); //int version will be called, default is int
    bb.aMethod((short)(10)); //short version will be called
    bb.aMethod((byte)(10)); //byte version will be called
    bb.aMethod(10.00); //double version will be called, default is double
    bb.aMethod(10.00f); //float version will be called
    float f = 10;
    bb.aMethod(f); //float version will be called
    double d = 10;
    bb.aMethod(d); //double version will be called
  }
}
 
OUTPUT
======
D:\JavaPrograms>java PracticeExercise1
long x: 10
int x: 10
short x: 10
byte x: 10
double x: 10.0
float x: 10.0
float x: 10.0
double x: 10.0
 

Java Method Overloading and Widening

Widening in Java method overloading comes into picture when an overloaded method does not find its exact matching signature. In that case, when an exact match isn't found, the JVM uses the method with the smallest argument that is wider than the parameter. For an example, if you have two overloaded method signatures as aMethod(short x) and aMethod(long x) and you make a call by passing a fixed value (literal) 10 to method aMethod as aMethod(10), aMethod(long x) will be called because Java by default treats integer literals int and JVM does not find aMethod(int x) and the next wider signature is aMethod(long x); therefore, JVM picks aMethod(long x). See the following example code and observe its output, where for aMethod(b) and aMethod(s), aMethod (int x) is called.

class WideningDemo
{
  public static void main (String[] args)
  {
    byte b = 5;
    short s = 5;
    long l = 5;
    float f = 5.0f;
 
	aMethod(b);
	aMethod(s);
	aMethod(l);
	aMethod(f);
  }
 
  static void aMethod (int x)
  {
    System.out.println("int version: " + x);
  }
 
  static void aMethod (long x)
  {
    System.out.println("long version: " + x);
  }
 
  static void aMethod (double x)
  {
    System.out.println("double version: " + x);
  } 
}
 
OUTPUT
======
D:\JavaPrograms>javac WideningDemo.java
 
D:\JavaPrograms>java WideningDemo
int version: 5
int version: 5
long version: 5
double version: 5.0

In above example the calls that use byte and the short arguments are implicitly widened to match the version of the aMethod() method that takes an int. Of course, the call with the long uses the long version of aMethod(), and finally, the call that uses a float is matched to the method that takes a double.

Java Method Overloading and Autoboxing

Every type in Java is either a reference type or a primitive type. A reference type is any class, interface, or array type. There are eight primitive types in Java, and each of these has a corresponding library class of reference type. These classes are called wrapper classes. Conversion of a primitive type to the corresponding reference type is called boxing and conversion of the reference type to the corresponding primitive type is called unboxing. This conversion is taken care by JVM and it is called autoboxing.

While overloading methods in Java you may come across such a situation where one signature takes wrapper reference type as formal argument and another signature takes a wider formal argument. For example, aMethod(Integer x) and aMethod(long x). Here, Integer is a wrapper reference type. If you call overloaded aMethod with an int argument, which method do you think will be called? The answer is that the compiler will choose widening over boxing, so the output will be long version: 5 of the following piece of code:

class BoxingWideningDemo
{
  public static void main (String[] args)
  {
    int l = 5;
    aMethod(l);
  }
 
  static void aMethod (Integer x)
  {
    System.out.println("int version: " + x);
  }
 
  static void aMethod (long x)
  {
    System.out.println("long version: " + x);
  }
}
 
OUTPUT
======
long version: 5

Widening beats boxing because autoboxing feature was added to Java in J2SE 5.0 release. So, Java 5's designers decided that pre-existing code should not break and it should function the way it used to. Since widening capability already existed; therefore, an overloaded method gives preference to widening over boxing.

Java Method Overloading and Var-args

Widening gets more priority over var-args because, once again, even though each invocation will require some sort of conversion, the compiler chooses the older style before it chooses the newer style, keeping existing code more robust. The following piece of code demonstrates widening over var-args:

class VarargsDemo
{
  public static void main (String[] args)
  {
    byte b = 5;
    aMethod(b, b);
  }
 
  static void aMethod (byte... b)
  {
    System.out.println("byte, byte");
  }
 
  static void aMethod (int b, int c)
  {
    System.out.println("int, int");
  }
}
 
OUTPUT
======
int, int

Widening Reference Variables in Java Method Overloading

As of now we have seen that widening of primitive types is legal and it takes priority over autoboxing and var-args. Likewise, is widening of reference variables also possible? The answer is YES if reference variables have IS~A relationship through inheritance. In that case, super class reference variable can hold subclass variable.

Now, you may be thinking, is widening of wrapper class variables also possible? The answer is NO because wrapper classes are peers they have no IS~A relationship with one another. You can not assign a Byte variable to a Long variable.

Java Method Overloading: Widening and Boxing Together

We have seen one step conversion for overloaded methods in widening and var-args but if widening and boxing happen together then would compiler be able to do method invocation conversion? The answer is NO. Let's take a look at the following piece of code and the error it gives.

class WideningAndBoxing
{
  public static void main (String[] args)
  {
    byte b = 5;
    aMethod(b);
  }
 
  static void aMethod (Long b)
  {
    System.out.println("Long");
  }
}
 
OUTPUT
======
D:\JavaPrograms>javac WideningAndBoxing.java
WideningAndBoxing.java:6: error: method aMethod in class WideningAndBoxing cannot be applied to given types;
    aMethod(b);
    ^
  required: Long
  found: byte
  reason: actual argument byte cannot be converted to Long by method invocation conversion
1 error
 

In above example, as you can see widening and boxing don't work if they are mixed. But strangely boxing followed by widening is acceptable iff this is passed to a reference of type Object. The following piece of code may seem strange to you but works fine.

class WideningAndBoxing
{
  public static void main (String[] args)
  {
    byte b = 5;
 
	// b is first widened to Byte and then Byte passed to Object 
    aMethod(b);
  }
 
  static void aMethod (Object b)
  {
    //Object b is typecasted to Byte and then printed 
    Byte bb = (Byte)b;
    System.out.println("b: " + bb);
  }
}
 
OUTPUT
======
D:\JavaPrograms>javac WideningAndBoxing.java
 
D:\JavaPrograms>java WideningAndBoxing
b: 5

Java Method Overloading: Concluding Remarks

You are really patient enough reader if you have come reading to this point. Following are the concluding points will certainly help you while overloading methods in Java.

  • Widening of primitive types uses the immediate next wider type method argument, if exact match is not found.
  • Boxing and var-args are compatible with method overloading if they are used individually.
  • One wrapper type can't be widened to another. For example, Integer can't be widened to Long
  • Widening followed by boxing does not work. For example, an int can't be passed to a Long.
  • Boxing followed by widening works only in one situation if boxed argument is passed to an Object type reference. An int can be passed to an Object, via Integer.
  • Var-args can be combined with either widening or boxing, but carefully. ☺

Practice Exercises

  1. Why can't one wrapper type be be widened to another? For example, Integer can't be widened to Long.
  2. Why can't an int be passed to a Long?
  3. Can static method be overloaded in Java? Can static method be overridden too? If yes, why? If no, why not?

Last Word

In this tutorial we discussed Java's method overloading feature. Method overloading belongs to polymorphism. Please do write us if you have any suggestion/comment or come across any error on this page. Thanks for reading!

References



Share this page on WhatsApp

Get Free Tutorials by Email

About the Author

is the founder and main contributor for cs-fundamentals.com. He is a software professional (post graduated from BITS-Pilani) and loves writing technical articles on programming and data structures.