我有以下用Java编写的说明性代码。它显示了覆盖不同自行车的introduceYourself()
方法。
public class Bicycle{
public void introduceYourself(){
System.out.println("Hello I am just a bicycle.");
}
}
public class MountainBike extends Bicycle{
public void introduceYourself(){
System.out.println("Hello I am a mountain bike and I love going outdoors.");
}
}
public class CityBike extends Bicycle{
public void introduceYourself(){
System.out.println("My name is city bike and I prefer calm trips.");
}
}
就像我期望的那样,以下代码为每个运行时对象调用introduceYourself()
方法,尽管这些变量被声明为基类Bicycle
类。如果我要将Bicycle或Bicycle子类型对象添加到数组并在循环上调用该方法,这将非常有用。
public class HelloWorld{
public static void main(String []args){
Bicycle b1 = new Bicycle();
Bicycle b2 = new MountainBike();
Bicycle b3 = new CityBike();
b1.introduceYourself(); // Output: Hello I am just a bicycle.
b2.introduceYourself(); // Output: Hello I am a mountain bike and I love going outdoors.
b3.introduceYourself(); // Output: My name is city bike and I prefer calm trips.
}
}
但是,我无法理解其他代码的行为。我有以下类,它们再次显示继承,但具有不同签名的方法(重载):
public class A{
public int calc (double num){
return (int)(num + 1);
}
}
public class B extends A{
public int calc (long num){
return (int)(num + 2);
}
}
public class C extends B{
public int calc (int num){
return (num + 3);
}
}
public class D extends C{
public int calc (float num){
return (int)(num + 4);
}
}
主方法中的以下代码:
public class HelloWorld{
public static void main(String []args){
int num1 = 10;
long num2 = 10;
A a1 = new D();
D d1 = new D();
System.out.println(a1.calc(num1)); // Output: 11
System.out.println(a1.calc(num2)); // Output: 11
System.out.println(d1.calc(num1)); // Output: 13
System.out.println(d1.calc(num2)); // Output: 12
}
}
为什么a1
引用的对象(声明类型为A
和运行时类型为D
)调用A
中声明的方法而不是最合适的方法一个(通过签名)已知其类D
的运行时对象? (另外,我认为有一个自动转换,因为参数类型不一样。)为什么它看起来与Bicycle示例有如此不同?感谢。
答案 0 :(得分:1)
多态性只有在你覆盖的情况下才会接管。在这里,当您声明:
时,您将重载不同的方法 A a1 = D();
请记住,父类对子类的方法一无所知,但是子类知道父类的方法。所以在这里你可以用D代替A,但你不能称之为D的方法。很抱歉,如果我的英语很糟糕,但TLDR:A只知道1方法calc(double num),因为double num也可以接受int和long,这就是函数工作的原因。否则它就行不通。
让我们在第一个例子中说你在CityBike类中有一个方法introductionYourSelf(String name),你可以这样做:
Bicycle bike = new CityBike();
bike.introduceYourSelf("I'm a city bike"); //error - Bicycle does not have method with argument string
答案 1 :(得分:0)
B,C,D中的计算方法不会覆盖A中的计算方法,因此当您通过a1(它' sa A)调用计算时,它实际上调用A中的方法定义(公共int calc(double num)),结果是11.(也就是说,a1被视为A,而不是D.并注意A中只定义了一个方法)
但是当你通过d1调用calc时,它定义了4个版本的calc,结果取决于你的参数类型和参数。
答案 2 :(得分:0)
为什么a1引用的对象(声明类型为A和 运行时类型D)调用在A中声明的方法而不是 最合适的......
实际上,它并不是最合适的。
考虑Widening primitive conversions,它将用于选择最合适的方法。
可能的转换是:
byte to short,int,long,float或double
缩短为int,long,float或double
char to int,long,float或double
int to long,float或double
长期漂浮或加倍
float to double
您希望选择以float为参数的方法。但是正如您在上面的列表中看到的那样,double绝不能适合另一种类型而不是... double。
JLS 15.12.2.5关闭审讯:
非正式的直觉是一种方法更具体 另一个如果可以传递第一个方法处理的任何调用 没有编译时错误的另一个。
因此,由于float可以传递给double而double不能传递给float,因此选择A中的方法是最合适的。
为什么它看起来与自行车示例的行为如此不同?
在自行车示例中,您将覆盖方法,而在第二个示例中,您正在超载。当重载最合适的方法时(根据JLS 15.12.2.5),当覆盖时,你将调用"最近的"运行时对象的方法。