Reusing the same method name in the same class or subclass but with different arguments (and optionally, a different return type) is called Method Overloading in Java.
There are certain rules for overloading, the below code points out all of the rules:
In short, the only rule you MUST obey is to change the argument list of the overloaded method, the rest are all optional.
Invoking Overloaded Methods
The output of the above program is:
In the Animal version
In the Horse version
In the Animal version
Notice the call doStuff(animalRefToHorse)
, here the Animal
version of doStuff()
is called despite the actual
object being passed is of a Horse
. The reference type (not the object type) determines which overloaded method is
invoked.
To summarize, which overridden version of the method to call (in other words, from which class in the inheritance tree) is decided at runtime based on object type, but which overloaded version of the method to call is based on the reference type of the argument passed at compile time.
Therefore, polymorphism doesn’t determine which overloaded version is called, polymorphism does come into play when the decision is about which overridden version of a method is called.
The confounding part in method overloading
I will show you different scenarios after which you can answer any question related to method matching (which method will be invoked).
The 3 factors that can make overloading a little tricky (written in order of preference):
- Widening
- Autoboxing
- Var-args
The output of the above program is float float float float int,int Long,Long
.
From the output it is clear that the JVM prefers widening over autoboxing and var-args. In every case, when an exact match isn’t found, the JVM uses the method with the smallest argument that is wider than the parameter.
Now look at the call go(l, l)
at last, this invokes go(Long x, Long y)
rather than go(long... x)
which clearly shows
JVM prefers boxing over var-args.
Widening reference variables
Reference widening depends on inheritance, in other words if it passes the IS-A test then no harm. Consider the below code:
The above code compiles fine as Dog
can widen into an Animal
because it passes the IS-A test. If in case, Dog
didn’t have extended Animal
then widening wouldn’t be possible and the code wouldn’t compile.
Similarly, you cannot widen Integer
to Long
but you can widen int
to long
.
Combine Widening with Boxing
Let’s see what happens when the compiler has to widen and then autobox the parameter for a match to be made.
The above program fails to compile, the JVM does not widen and then box. It may be because widening existed in the earlier versions of Java and it wanted a method that is invoked via widening shouldn’t lose out to a newly created method that relies on boxing. In other words, Java designers wanted the preexisting code should function the way it used to.
Now imagine if JVM tried to box first, the byte
would have been converted to a Byte
. Now we’re back to trying to widen a
Byte
to a Long
, and of course, the IS-A test fails.
So both of the ways didn’t work.
Now let’s see another program when the compiler has to autobox and then widen the parameter for a proper match.
The above code compiles and produces the output 5
. Firstly, the byte b
was boxed to a Byte
. And then the Byte
reference was widened to an Object
(since Byte
extends Object
). So, the go()
method got an Object
reference
that actually refers to a Byte
object.
From the above 2 examples its certain that the JVM can never widen and then box but can box and then widen.
Combine both Widening and Boxing with Var-args
The above code compiles fine and produces the output:
long...
Integer...
Object...
From the result, its clear that we can successfully combine var-args with either widening or boxing.
Q&A
Q1. Consider the below program in which a method is both overridden and overloaded.
Figure out which version of eat()
will run on each of the invocation made?
A. Animal ah = new Horse();
ah.eat();
B. Horse he = new Horse();
he.eat("Apples");
C. Animal a2 = new Animal();
a2.eat("treats");
D. Animal ah2 = new Horse();
ah2.eat("Carrots");