Reputation: 10963
I do not understand this Java behavior. I have two classes:
class C1 {
public void m1(double num) {
System.out.println("Inside C1.m1(): " + num);
}
}
class C2 extends C1 {
public void m1(int num) {
System.out.println("Inside C2.m1(): " + num);
}
}
And it is my main:
public class Main {
public static void main(String[] args) {
C1 c = new C2();
c.m1(10);
}
}
And the result was:
Inside C1.m1(): 10.0
When I expected:
Inside C2.m1(): 10
Also when I try to complete the code syntax, I found this:
Where is the other m1 of C2 class?
I also check the bytecode of my Main.class and I saw this:
Compiled from "Main.java"
public class com.company.Main {
public com.company.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: new #2 // class com/company/C2
3: dup
4: invokespecial #3 // Method com/company/C2."<init>":()V
7: astore_1
8: aload_1
9: ldc2_w #4 // double 10.0d
12: invokevirtual #6 // Method com/company/C1.m1:(D)V
15: return
}
The bytecode tell me that it will invoke the C1.m1 (D)V (line 12).
Why the method of C1? I am trying to understand this behavior.
Upvotes: 17
Views: 4350
Reputation: 1672
Java chooses the most specific applicable type. In this case m1(int) is not applicable. Emphasis on the reference of class that hold object of same class(c1) or an object of any sub classes of class(c2) & method name and parameter list .
Your method with double parameters is being called because double takes priority over int. This is because an int can be assigned to a double, but not the other way around.
So there are so many things to be consider at the (run) time of method call.
Yes for ur case your main class should be like this
public static void main(String[] args) {
C1 c = new C2();
c.m1(10);
((C2) c).m1(10);
//or
C2 cobj = new C2();
cobj.m1(10);
}
**OutPut**
Inside C1.m1(): 10.0
Inside C2.m1(): 10
Inside C2.m1(): 10
Upvotes: 0
Reputation: 2940
If you call c.m1(10.0) it will call the method of the ancestor as you were expecting at first.
You're doing method overloading in your example (that is adding more methods with the same name and different signature) instead of method overriding (that is changing the implementation of an ancestor's method at a descendent by redeclaring it with the same signature, AKA same name and same type of result and of method arguments - argument names shouldn't matter).
Upvotes: 3
Reputation: 125
By looking at your code, you are not taking advantage of inheriting to get the answer you want. You have to change this line
C1 c = new C2();
to
C2 c = new C2();
Upvotes: 2
Reputation: 69035
Method signatures for both methods are different.
public void m1(double num)
public void m1(int num)
So there is no overriding in this case. Now when you say
C1 c = new C2();
c.m1(10);
at compile time, it will se reference is of type C1
which has method public void m1(double num)
which is compatible with 10 [int in expanded to double]. So int is promoted to double, and the corresponding method is called (which is also what you see in the bytecodes).
Upvotes: 4
Reputation: 11173
Since C1.m1(double num)
is a public method, it inherited C2. So your C2 also has a method, m1(double num)
, and that's why it is called. From main()
you actually called C2.m1(double num)
.
Note: Now at class C2
you have two overloaded methods -
m1(int num)
and m1(double num)
. And C2.m1(int num)
is a different method from C2.m1(double num)
.
Upvotes: 1
Reputation: 3638
Java does method dispatch on static types, and your variable c
is of type C1
, so m1(int)
is not visible, and your 10
is cast to double
.
Upvotes: 6
Reputation: 1009
It's because of the parameters. The method you call is a method with a double parameter. m1 inside of C2 is not overriding this, instead it's overLOADING it.
If you want to call m1 in C2, you have to cast the reference such that the compiler accepts what you're doing.
Upvotes: 9
Reputation: 15212
The reason why you see the output as Inside C1.m1(): 10.0
and not Inside C1.m1(): 10
or Inside C2.m1(): 10.0
is because :
m1
in C2
. You are overloading the m1(doube)
method that you inherited from C1
to m1(int)
instead.C2
class now has two m1
methods. One that is inherited
from C1
and has the signature m1(double)
and one that is overloaded in C2
and has the signature m1(int)
c.m1(10)
, it resolves this call based on the reference type. Since the reference type is C1
, the compiler is going to resolve this call to m1(double)
in C1
. m1(double)
in C2
which is the method inherited from C1
. (As explained in point 2)There are two ways in which the m1(int)
method can be called :
((C2)c).m1(10);
OR
C2 c = new C2();
c.m1(10);
Upvotes: 8
Reputation: 353
You cannot see methods of C2, because you instance variable is declared as C1 and also because they don't have the same signature. You have double parameter in one method and in second a int type which makes them for JVM completely different methods (so no inheritance will work here).
So if you have int type in C1 method then you need to have also int type in C2 method, then JVM will run method from C2 like you wanted.
Also you can cast variable to C2 type then you will be able to access to methods of C2.
Upvotes: 2
Reputation: 77234
Your two methods named m1
do not have the same signature; the one in the superclass takes a double
, and the one in the subclass takes an int
. This means that the compiler will select the method signature to call based on the compile-time type of your variable, which is C1
, and will call m1(double)
. Since at runtime the class C2
doesn't have an overriding version of m1(double)
, the version from C1
is invoked.
The rule is that method signatures are computed at compile time based on compile-time types; method calls are dispatched at runtime based on matching signatures.
Upvotes: 16