Overloading is compile-time polymorphism. Really?
Overloaded methods can still be overridden, if that is what you ask.
Overloaded methods are like different families, even though they share the same name. The compiler statically chooses one family given the signature, and then at run time it is dispatched to the most specific method in the class hierarchy.
That is, method dispatching is performed in two steps:
- The first one is done at compile time with the static information available, the compiler will emit a
call
for the signature that matches best your current method parameters among the list of overloaded methods in the declared type of the object the method is invoked upon. - The second step is performed at run time, given the method signature that should be called (previous step, remember?), the JVM will dispatch it to the most concrete overridden version in the actual type of receiver object.
If the method arguments types are not covariant at all, overloading is equivalent to having methods names mangled at compile time; because they are effectively different methods, the JVM won't never ever dispatch them interchangeably depending on the type of the receiver.
What is polymorphism?
Acc. to me: if an entity can be represented in more than one forms, that entity is said to exhibit polymorphism.
Now, lets apply this definition to Java constructs:
1) Operator overloading is compile time polymorphism.
For example, +
operator can be used to add two numbers OR to concatenate two strings. it's an example of polymorphism strictly saying compile-time polymorphism.
2) Method overloading is compile time polymorphism.
For example, a method with same name can have more than one implemntations. it's also a compile-time polymorphism.
It's compile-time because before execution of program compiler decides the flow of program i.e which form will be used during run-time.
3) Method overriding is run-time polymorphism.
For example, a method with same signature can have more than one implemenations. it's a run time polymorphism.
4) Base class use in place of derived class is run time polymorphism.
For example, an interface
reference can point to any of it's implementor.
It's run-time because the flow of program can't be known before execution i.e. only during run-time it can be decided that which form will be used.
I hope it clears a bit.
Every 'Greeter' class has 3 virtual methods: void greetMe()
, void greetMe(String)
, and void wishLuck()
.
When you call greeter.greetMe()
the compiler can work out which one of the three virtual methods should be called from the method signature - ie. the void greetMe()
one since it accepts no arguments. Which specific implementation of the void greetMe()
method is called depends on the type of the greeter
instance, and is resolved at run-time.
In your example it's trivial for the compiler to work out which method to call, since the method signatures are all completely different. A slightly better example for showing the 'compile time polymorphism' concept might be as follows:
class Greeter {
public void greetMe(Object obj) {
System.out.println("Hello Object!");
}
public void greetMe(String str) {
System.out.println("Hello String!");
}
}
Using this greeter class will give the following results:
Object obj = new Object();
String str = "blah";
Object strAsObj = str;
greeter.greetMe(obj); // prints "Hello Object!"
greeter.greetMe(str); // prints "Hello String!"
greeter.greetMe(strAsObj); // prints "Hello Object!"
The compiler will pick out the method with the most specific match using the compile-time type, which is why the 2nd example works and calls the void greetMe(String)
method.
The last call is the most interesting one: Even though the run-time type of strAsObj is String
, it has been cast as an Object
so that's how the compiler sees it. So, the closest match the compiler can find for that call is the void greetMe(Object)
method.