While the main principle of polymorphism is decoupling "what from who" in term of types
, but what confuses me how does method-call mechanism finds out and calls the correct method body in polymorphism.
Since in java all method binding is late-binding
unless the method is static
, final
or private
, and late-binding is done by JVM which precomputes method table
for each class and then do a table look up during runtime in normal method call.
But the same thing happens during polymorphism too. For example
Suppose I've a Generic class Cycle
with a ride()
method
class Cycle {
public void ride(){
System.out.println("I'm Riding generic Cycle()");
}
}
And i have three Specialized Class Bicycle
Tricycle
and Unicycle
which extends Generic class Cycle
and overrides its ride()
method.
class Bicycle extends Cycle {
public void ride() {
System.out.println("I'm riding Bicycle");
}
}
class Tricycle extends Cycle{
public void ride() {
System.out.println("I'm riding Tricycle ");
}
}
class Unicycle extends Cycle {
public void ride() {
System.out.println("I'm Riding Unicycle ");
}
}
This is the TestRide
class to Test the above Polymorphism.
public class TestRide {
public static void ride(Cycle c){
c.ride();
}
public static void main(String[] args){
Cycle Cycling = new Cycle();
ride(Cycling);
Bicycle bi = new Bicycle();
ride(bi);
Tricycle tri = new Tricycle();
ride(tri);
Unicycle uni = new Unicycle();
ride(uni);
}
}
The Output is
I'm Riding generic Cycle()
I'm riding Bicycle
I'm riding Tricycle
I'm Riding Unicycle
Byte Code:
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=5, args_size=1
0: new #17 // class com/polymorphism/Cycle
3: dup
4: invokespecial #24 // Method com/polymorphism/Cycle."
<init>":()V
7: astore_1
8: aload_1
9: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
12: new #27 // class com/polymorphism/Bicycle
15: dup
16: invokespecial #29 // Method com/polymorphism/Bicycle
."<init>":()V
19: astore_2
20: aload_2
21: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
24: new #30 // class com/polymorphism/Tricycle
27: dup
28: invokespecial #32 // Method com/polymorphism/Tricycl
e."<init>":()V
31: astore_3
32: aload_3
33: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
36: new #33 // class com/polymorphism/Unicycle
39: dup
40: invokespecial #35 // Method com/polymorphism/Unicycl
e."<init>":()V
43: astore 4
45: aload 4
47: invokestatic #25 // Method ride:(Lcom/polymorphism/
Cycle;)V
50: return
Even in the bytecode its just as usual method call with invokestatic
and invokespecial
while i thought it would use invokedynamic
to figure out the version of the method that is appropriate for the actual type of the object. But that was not the case.
So how does Java figure out the actual method call during polymorphism while we just pass an upcasted Object in the ride()
method like ride(bi)
in TestRide
class ?
EDIT: RIDE method ByteCode
public static void ride(com.polymorphism.Cycle);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokevirtual #16 // Method com/polymorphism/Cycle.r
ide:()V
4: return
First off invokedynamic
is for Java 8 lambdas and non-Java code, so you can forget about that.
Apart from that, there are four invoke instructions (invokespecial
, invokestatic
, invokevirtual
, and invokeinterface
). You can see the precise semantics in the JVM sepcification, but the bottom line is that invokevirtual
and invokeinterface
are virtual method calls, i.e. the actual method called is chosen at runtime based on the conrete type of the target.
The only virtual call in your code is in TestRide.ride. The listed target is Cycle.ride:()V
. However, since it is a virtual call, the JVM will check the actual type of the first argument at runtime and call the most derived version of that method.
This is similar to virtual method calls in C++, except that the abstraction of the JVM and JIT compilation allows the potential for more optimized implementations.
Also note that this is not to be confused with method overloading, which is a form of compile-time polymorphism. For overloaded methods, the compiler chooses which one to call based on the compile time type of the arguments.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With