所以这个问题是关于继承和方法覆盖。 具体来说:子类具有与父类相同的名称方法但具有不同签名的情况,例如:
class A has methodX(String arg)
class B extends A has methodX(int arg)
在正常情况下,将根据参数调用正确的方法。
但是在下面的代码中我遇到了一些我无法解释的奇怪行为:
static class A {
public void method1() {
System.out.println("m1.A");
}
public void method4(A arg) { //Original method4
System.out.println("m4.A");
}
}
static class B extends A {
public void method1() {
System.out.println("m1.B");
}
}
static class C extends B {
public void method4(A arg) { //Override method4 from Class A
System.out.println("m4.C");
}
}
static class E extends C {
public void method1() {
System.out.println("m1.E");
}
public void method4(E arg) { //NO OVERRIDE: Same name, but different method4 than in class A or C
System.out.println("m4.E");
}
}
public static void main(String[] args) {
A va = new A();
B vb = new B();
C vc = new C();
E ve = new E();
//At this point everything is fine
ve.method4(ve); //Calls method4 from class E based on parameter type - CORRECT
ve.method4(va); //Calls method4 from class C based on parameter type - CORRECT
//After this code strange things happen
vc = new E();
vb = vc;
vb.method1(); //Output: m1.E; method1 from class E is called - CORRECT
vb.method4(vb); //Output: m4.C; method4 from class C is called - why?
vc.method1(); //Output: m1.E; method1 from class E is called - CORRECT
vc.method4(vc); //Output: m4.C; method4 from class C is called - why?
vc.method4(ve); //Output: m4.C; method4 from class C is called - why?
}
所以上面的程序输出是:
m4.E
m4.C
m1.E
m4.C //why? Expected: m4.E
m1.E
m4.C //why? Expected: m4.E
m4.C //why? Expected: m4.E
的行为
vb and vc
是我无法理解的。有什么想法吗?
答案 0 :(得分:3)
我猜你打算期待 m4.E
。但是不要忘记重载解析是在编译时执行的,而不是在执行时执行。
C
和B
都没有可用的method4(E)
方法,因此编译器会解析对method4(A)
方法的调用... 不是< / em>被E
覆盖。您在问题中m4.C // why?
拨打的所有电话都会调用带有签名method4(A)
的方法,并在E
的实例上调用。现在E
没有覆盖method4(A)
,所以它留在C
中的实现,它会打印m4.C
。
这里没什么奇怪的。
答案 1 :(得分:3)
您没有在课程method4
和课程C
之间覆盖E
:他们有不同的签名。
使用method1
时,它不带参数,因此子类将覆盖现有方法。但编译器看到了
public void method4(E arg);
和
public void method4(C arg);
完全不同(就像他们有不同的名字一样)。
您应该尝试使用@Override
在子类中注释您的方法。您会发现它会在method1
上允许它并在method4
上投诉。
对于最后几行,编译器将
method4
中查找C
,因为vc
的声明类型为C
。method4
,看看它是A
的参数。在运行时,JVM将
vc
的子类中被覆盖。C
。我认为混乱来自于这三条线紧密相连:
C vc = new C();
// ...
vc = new E();
// ...
vc.method4(vc); //Output: m4.C; method4 from class C is called - why?
您和我可以看到vc
的实际类型为E
,因为您刚刚创建了new E()
并将其分配给vc
。但是通常不能期望编译器进行这种推断:在实际应用程序中,代码可能要复杂得多,而且在编译时没有任何确定性可以解析为实际类型的代码。中间线可能是
vc = runningOnAThursday ? new E() : new C();
所有编译器都可以查看声明的类型,即C
。只有在运行时才能检查实际的类型(因此,有关重写的方法)。
答案 2 :(得分:1)
你让我失去了5个类之间的4种方法,但我可以猜测你期望调用实例的实际类型的方法。
但是,这不是一个有效的假设,因为Java只能使用Object的最具体的已知类型来调用该方法。
这是一个更简单的例子,演示了我在说什么:
public class Test {
public static void method(String obj){
System.out.println("String");
}
public static void method(Object obj){
System.out.println("Object");
}
public static void main(String... args){
Object string = "testing";
method(string);
}
}
请注意,此代码调用方法(Object)函数,即使字符串变量的实际类型是String。这是因为Java知道字符串变量的最具体的类型是Object,而不是String。