编译时行为与 运行 时多态性(或方法签名)
Behaviour of compile-time vs run-time polymorphism (or method signature)
我用 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
class。如果我要将 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.
}
}
但是,我无法理解其他代码的行为。我有以下 classes,它们再次显示继承,但方法具有不同的签名(重载):
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);
}
}
并且main方法中的代码如下:
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
中声明的方法而不是由其 class D
的 运行time 对象所知的最合适的(通过签名)? (另外,我想有一个自动转换,因为参数类型不一样。)为什么它的行为看起来与 Bicycle 示例如此不同?谢谢。
B、C、D中的calc方法不会覆盖A中的calc方法,所以当你通过a1(它是一个A)调用calc时,它实际上调用了A中定义的方法(public int calc(double num)), 所以结果是11.(也就是说a1被当做A, 不是D. 注意A里只有一个方法定义)
但是当你通过 d1 调用 calc 时,它定义了 4 个版本的 calc,结果取决于你的参数类型和参数。
Why does the object referenced by a1 (with declared type A and
run-time type D) call the method declared in A instead of the
most-appropiate one...
其实这不是最合适的
考虑 Widening primitive conversions 将用于选择最合适的方法。
可能的转换是:
byte 到 short、int、long、float 或 double
short 到 int、long、float 或 double
char 到 int、long、float 或 double
int 到 long、float 或 double
long to float or double
浮动到两倍
您希望选择以浮点数为参数的方法。但是,正如您在上面的列表中看到的那样,double 永远不能适合其他类型... double.
JLS 15.12.2.5 关闭审讯:
The informal intuition is that one method is more specific than
another if any invocation handled by the first method could be passed
on to the other one without a compile-time error.
因此float可以传给double,double不能传给float,所以选择A的方法最合适。
Why does it seem to behave so differently from the Bicycle example?
在自行车示例中,您重写了方法,而在第二个示例中您重载了方法。重载最合适的方法时(根据 JLS 15.12.2.5),而重写时您将调用运行时对象的 "nearest" 方法。
多态性仅在您覆盖时才会接管。在这里你重载了不同的方法,所以当你声明时:
A a1 = D();
记住,父class对子class的方法一无所知,但子class知道父class的方法。所以在这里你可以用 D 代替 A,但是你不能调用 D 的方法。对不起,如果我的英语很烂,但是 TLDR:A 只知道 1 种方法 calc(double num) 并且因为 double num 也可以接受 int 和 long,这就是该函数起作用的原因。否则它不会工作。
假设在第一个示例中,您在 class CityBike 中有一个 introduceYourSelf(String name) 方法,您可以这样做:
Bicycle bike = new CityBike();
bike.introduceYourSelf("I'm a city bike"); //error - Bicycle does not have method with argument string
我用 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
class。如果我要将 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.
}
}
但是,我无法理解其他代码的行为。我有以下 classes,它们再次显示继承,但方法具有不同的签名(重载):
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);
}
}
并且main方法中的代码如下:
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
中声明的方法而不是由其 class D
的 运行time 对象所知的最合适的(通过签名)? (另外,我想有一个自动转换,因为参数类型不一样。)为什么它的行为看起来与 Bicycle 示例如此不同?谢谢。
B、C、D中的calc方法不会覆盖A中的calc方法,所以当你通过a1(它是一个A)调用calc时,它实际上调用了A中定义的方法(public int calc(double num)), 所以结果是11.(也就是说a1被当做A, 不是D. 注意A里只有一个方法定义)
但是当你通过 d1 调用 calc 时,它定义了 4 个版本的 calc,结果取决于你的参数类型和参数。
Why does the object referenced by a1 (with declared type A and run-time type D) call the method declared in A instead of the most-appropiate one...
其实这不是最合适的
考虑 Widening primitive conversions 将用于选择最合适的方法。
可能的转换是:
byte 到 short、int、long、float 或 double
short 到 int、long、float 或 double
char 到 int、long、float 或 double
int 到 long、float 或 double
long to float or double
浮动到两倍
您希望选择以浮点数为参数的方法。但是,正如您在上面的列表中看到的那样,double 永远不能适合其他类型... double.
JLS 15.12.2.5 关闭审讯:
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time error.
因此float可以传给double,double不能传给float,所以选择A的方法最合适。
Why does it seem to behave so differently from the Bicycle example?
在自行车示例中,您重写了方法,而在第二个示例中您重载了方法。重载最合适的方法时(根据 JLS 15.12.2.5),而重写时您将调用运行时对象的 "nearest" 方法。
多态性仅在您覆盖时才会接管。在这里你重载了不同的方法,所以当你声明时:
A a1 = D();
记住,父class对子class的方法一无所知,但子class知道父class的方法。所以在这里你可以用 D 代替 A,但是你不能调用 D 的方法。对不起,如果我的英语很烂,但是 TLDR:A 只知道 1 种方法 calc(double num) 并且因为 double num 也可以接受 int 和 long,这就是该函数起作用的原因。否则它不会工作。
假设在第一个示例中,您在 class CityBike 中有一个 introduceYourSelf(String name) 方法,您可以这样做:
Bicycle bike = new CityBike();
bike.introduceYourSelf("I'm a city bike"); //error - Bicycle does not have method with argument string