Reputation: 2463
I wrote the following piece of code:
class Plane {}
class Airbus extends Plane {}
public class Main {
void fly(Plane p) {
System.out.println("I'm in a plane");
}
void fly(Airbus a) {
System.out.println("I'm in the best Airbus!");
}
public static void main(String[] args) {
Main m = new Main();
Plane plane = new Plane();
m.fly(plane);
Airbus airbus = new Airbus();
m.fly(airbus);
Plane planeAirbus = new Airbus();
m.fly(planeAirbus);
}
}
And the result is:
I'm in a plane
I'm in the best Airbus!
I'm in a plane
Unsurprisingly the two first invocations give I'm in a plane
and I'm in the best Airbus!
respectively.
Plane planeAirbus = new Airbus();
The method treats this object as a Plane, even though the real object is an Airbus. Even when I add abstract
to class Plane
, nothing changes and the result of last invocation is still I'm in a plane
So the question is why polymorphism doesn't work in method arguments and invocations? Is there any purpose of that? How does it work?
Upvotes: 17
Views: 6263
Reputation: 8651
Java overloading is compile time polymorphism. So, when you declare planeAirbus
as Plane
, it will invoke fly(Plane)
.
In fact, class Main
shouldn't know than Plane
and Airbus
can fly.
Better design of it:
public interface Flyable{
void fly();
}
public Plane implements Flyable{
void fly(){
//do something
}
}
public Airbus implements Flyable{
void fly(){
//do something
}
}
And then in Main
class
public static void main(String[] args) {
Flyable plane = new Plane();
plane.fly();
Flyable airbus = new Airbus();
airbus.fly();
}
Upvotes: 3
Reputation: 10653
Method overloading type polymorphism is determined at the compile time in Java.
Meaning that Java has to infer type of the method parameters from the reference type they represent, as it has no idea about the type of object they hold at the compile time.
We can argue that it's pretty clear in this case that reference of Plane type holds Airbus type instance. However it's not that simple, as the Airbus instance itself could have been a method parameter, which could hold any subclass instance or Airbus instance itself.
Only safe bet is to not parse through the parent chain and take the reference for it's face value that is actual reference variable type. Another way of doing this could have been by implementing method overloading same as overriding and by using run time binding of objects for resolution. I don't know why it wasn't done this way as it would have made method overloading and overriding more uniform.
Following are the references from JLS Overloading
When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (§15.12.2). If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4).
Upvotes: 4
Reputation: 839
Reason for the outputs following output:
I'm in a plane
I'm in the best Airbus!
I'm in a plane
Because it is performing overloading, and overloading is Static polymorphism or Compile Time Polymorhism. In overloading, class can have more than one methods with same name but different types of arguments. In your example Plane
is type for Plane planeAirbus = new Airbus();
Upvotes: 1
Reputation: 7332
The problem here is that Java does not support dynamic binding of method arguments. What you see is static binding, i.e. the overload of the method to call is chosen at compile time.
See also: Static Binding and Dynamic Binding
Upvotes: 11