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.
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:
public
, protected
, or private
) CAN also be same or different for overloaded methods.
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.
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
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
.
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.
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
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.
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
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.
Integer
can't be widened to Long
int
can't be passed to a Long
.Object
type reference. An int
can be passed to an Object
, via Integer
.Integer
can't be widened to Long
.
int
be passed to a Long
?
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!
Share this page on WhatsApp