我不明白这种Java行为。我有两个班:
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);
}
}
这是我的主要内容:
public class Main {
public static void main(String[] args) {
C1 c = new C2();
c.m1(10);
}
}
结果是:
Inside C1.m1(): 10.0
当我预料到:
Inside C2.m1(): 10
当我尝试完成代码语法时,我发现了这个:
C2类的其他m1在哪里?
我还检查了Main.class的字节码,我看到了:
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
}
字节码告诉我它会调用C1.m1(D)V(第12行)。
为什么C1的方法?我试图理解这种行为。
答案 0 :(得分:16)
您的两个名为m1
的方法没有相同的签名;超类中的一个采用double
,子类中的采用int
。这意味着编译器将根据变量的编译时类型(C1
)选择要调用的方法签名,并调用m1(double)
。由于在运行时,类C2
没有覆盖m1(double)
的版本,因此会调用C1
中的版本。
规则是方法签名是在编译时基于编译时类型计算的;方法调用在运行时根据匹配的签名进行调度。
答案 1 :(得分:10)
这是因为参数。您调用的方法是具有double参数的方法。 C2中的m1不会覆盖它,而是重载它。
如果要在C2中调用m1,则必须转换引用,以便编译器接受您正在执行的操作。
答案 2 :(得分:7)
您将输出视为Inside C1.m1(): 10.0
而不是Inside C1.m1(): 10
或Inside C2.m1(): 10.0
的原因是:
m1
中的方法C2
。您正在将从m1(doube)
继承的C1
方法重载为m1(int)
。C2
类现在有两种m1
方法。一个inherited
来自C1
并且具有签名m1(double)
,其中一个在C2
中重载并且具有签名m1(int)
c.m1(10)
时,它会根据引用类型解析此调用。由于引用类型为C1
,编译器将在m1(double)
中将此调用解析为C1
。 m1(double)
中C2
的调用,这是从C1
继承的方法。 (如第2点所述)有两种方法可以调用m1(int)
方法:
((C2)c).m1(10);
或强>
C2 c = new C2();
c.m1(10);
答案 3 :(得分:6)
Java对静态类型进行方法调度,而变量c
的类型为C1
,因此m1(int)
不可见,并且10
被强制转换为{{ 1}}。
答案 4 :(得分:4)
两种方法的方法签名都不同。
public void m1(double num)
public void m1(int num)
所以在这种情况下没有压倒一切。现在当你说
C1 c = new C2();
c.m1(10);
在编译时,它的引用类型为C1
,其方法public void m1(double num)
与10 [int in expanded to double]兼容。因此int被提升为double,并调用相应的方法(这也是你在字节码中看到的)。
答案 5 :(得分:3)
如果你打电话给c.m1(10.0),它会像你原先想象的那样调用祖先的方法。
你在你的例子中做了方法重载(即添加更多具有相同名称和不同签名的方法)而不是方法覆盖(即在后代改变祖先方法的实现)使用相同的签名,AKA相同的名称和相同类型的结果以及方法参数重新声明它 - 参数名称不应该重要。
答案 6 :(得分:2)
通过查看您的代码,您没有利用继承来获得您想要的答案。你必须改变这一行
C1 c = new C2();
到
C2 c = new C2();
答案 7 :(得分:2)
你看不到C2的方法,因为你的实例变量被声明为C1,也因为它们没有相同的签名。你在一个方法中有双参数,第二个是int类型,这使得它们对于JVM完全不同的方法(因此这里没有继承)。
因此,如果你在C1方法中有int类型,那么你需要在C2方法中也有int类型,然后JVM将像你想要的那样从C2运行方法。
此外,您可以将变量转换为C2类型,然后您就可以访问C2的方法。
答案 8 :(得分:1)
由于C1.m1(double num)
是一个公共方法,它继承了C2。所以你的C2也有一个方法,m1(double num)
,这就是它被调用的原因。来自main()
您实际呼叫C2.m1(double num)
。
注意:现在在课程C2
,您有两个重载方法 -
m1(int num)
和m1(double num)
。 C2.m1(int num)
是与C2.m1(double num)
不同的方法。
答案 9 :(得分:0)
Java选择最具体的适用类型。在这种情况下,m1(int)不适用。 强调保持同一类(c1)的对象的类的引用或类(c2)的任何子类的对象。方法名称和参数列表。
正在调用带有double参数的方法,因为double优先于int。这是因为int可以分配给double,但不是相反。
因此在方法调用的(运行)时间有很多事情需要考虑。
对于你的情况,你的主要课程应该是这样的
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