我在理解这段代码的行为时遇到了问题。a被定义为a,c被定义为c。然后,在公共类的末尾,a=c。当调用display()方法时,它会到达它的C版本。但是,当a调用f()时,它只到达a版本,尽管第一个参数(字节和长)更符合long而不是float。
这是一本书中的练习,但解释很少,或者根本不存在。
class A{
public void display(){
System.out.println("I am an A ");
}
public void f(double x){
System.out.println("A.f(double = " + x + ") ");
}
}
class C extends A{
public void display(){
System.out.println("I am a C ");}
public void f(long q){
System.out.println("C.f(long = " + q + ") ");}
}
public class PolySurStack{
public static void main(String Args[]){
byte bb =1; long q = 4; float x = 5.f;
System.out.println(" ** A **");
A a = new A(); a.display();
a.f(bb); a.f(x);
System.out.println();
System.out.println(" ** C **");
C c = new C(); c.display();
c.f(bb); c.f(q); c.f(x);
System.out.println();
a = c; a.display();
a.f(bb); a.f(q); a.f(x);
}
}
当您调用a.f(bb)
、a.f(q)
或a.f(x)
时,编译器只能从类A
(或A
的任何超类)中定义的方法签名中进行选择,因为a
是A
类型的引用变量。
因此,仅考虑public void f(double x)
。为了使public void f(long q)
成为重载解析的候选者,您必须在调用f()
之前将a
强制转换为类型C
,因为只有类C
定义了具有该签名的方法。
需要理解的重要一点是,方法重载解析发生在编译时。只有为其调用方法的引用变量的编译时类型才能确定哪些方法签名是方法重载解决方案的候选者,以及将选择哪个候选者。
我刚刚在另一个论坛上发现了这个:
过载:(函数名称相同,签名不同)
-
两个或多个方法在同一类中具有相同的名称和不同的分段,称为重载。
-
当您想要扩展功能时,会使用重载。
-
重载被称为编译时多态性
重写:(相同的函数名称但相同的签名)
-
父类和子类中具有相同方法名和相同分段的两个或多个方法称为重写。
-
当您想要重用现有的功能性时,将使用重写。
-
重写被称为运行时多态性
所以我的问题的答案似乎是,覆盖分辨率(如display())发生在运行时(在a=c之后),而重载分辨率(如f())则发生在编译时,此时a仍然是a。
我想。
我还发现了这个页面:https://beginnersbook.com/2013/04/runtime-compile-time-polymorphism/
明确并与此主题高度相关。
编译器选择的方法取决于声明的类型,而不是运行时类型
声明为变量A
的第一个系列只能调用A方法(无论实例化为A
的运行时类型是什么,都只能从Object
派生):
A a = new A();
a.f(bb); a.f(x);
对于第二个系列,编译器将具有与调用匹配的最特定参数的方法绑定,因为C
是A
,因此编译器可以在这里绑定这些方法的任何公共方法:
C c = new C();
c.f(bb); c.f(q); c.f(x);
但在最后一段可能会让您自己产生疑问的代码中,a
将C
称为运行时对象,而将A
称为声明类型:
A a = new A();
// ...
a = c;
a.f(bb); a.f(q); a.f(x);
因此,只能调用A
中定义的方法。
我会尽量澄清@eran的答案,这样你就可以理解了。
子类拥有其超类的所有方法,然后可能还有更多的方法。您有一个类型为A
的变量,其中存储了一个类型C
的对象。C
具有类A
中定义的所有方法,还有一个附加方法f(long q)
。A
不知道这个新方法,所以由于您将对象存储在A
的变量中,因此无法调用f(long q)
。
但是,可以调用display()
,因为它是在A
中定义的,但执行它的仍然是C
对象。