简体   繁体   English

编译时与运行时多态(或方法签名)的行为

[英]Behaviour of compile-time vs run-time polymorphism (or method signature)

I have the following illustrative code written in Java. 我有以下用Java编写的说明性代码。 It shows the overriding of the introduceYourself() method for the different bikes. 它显示了针对不同自行车的IntroductionYourself 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.");
    }
}

Just as I was expecting, the following code calls the introduceYourself() method for each run-time object , although the variables were declared as the base Bicycle class. 就像我期望的那样, 以下代码为每个运行时对象调用introduceYourself()方法 ,尽管变量被声明为基Bicycle类。 This would be useful if I were to add Bicycle or Bicycle subtype objects to an array and call the method on a loop. 如果我要将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.  
     }
}

However, I am having trouble understanding the behaviour of this other code. 但是,我很难理解其他代码的行为。 I have the following classes which, again, show inheritance, but methods with different signatures (overloading): 我有以下再次显示继承的类,但是具有不同签名(重载)的方法:

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);
    }
}

And the following code in the main method: 以及以下代码中的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
 }

} }

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 (by signature) known by its runtime object of class D ? 为什么由a1引用的对象(具有声明的类型A和运行时类型D )调用在A声明的方法,而不是其类D运行时对象所知的最合适的方法(通过签名)? (Also, I suppose there is an automated casting, since the argument type is not the same.) Why does it seem to behave so differently from the Bicycle example? (此外,由于参数类型不同,我想有一个自动强制转换。)为什么它的行为似乎与Bicycle示例不同? Thanks. 谢谢。

Polymorphism only takes over if you're overriding. 多态仅在您覆盖时才接管。 Here you're overloading different methods so when you declare: 在这里,您正在重载不同的方法,因此在声明时:

 A a1 = D();  

Remember, the parent class knows nothing about the child class's method, but the child class knows about the parent class's method. 记住,父类对子类的方法一无所知,但是子类对父类的方法一无所知。 So here you might substitute D for A, but you cannot call D's method. 因此,在这里您可以用D代替A,但是不能调用D的方法。 Sorry if my english sucks, but TLDR: A only knows 1 method calc(double num) and because double num can also accept int and long, that's why the function works. 抱歉,如果我的英语很烂,但是TLDR:A只知道1个方法calc(double num),并且因为double num也可以接受int和long,所以这是函数起作用的原因。 Otherwise it wouldn't work. 否则它将无法正常工作。

Lets say in the first example you have a method introduceYourSelf(String name) in class CityBike and you do something like this: 可以说在第一个示例中,您在CityBike类中有一个方法IntroductionYourSelf(String name),您可以执行以下操作:

 Bicycle bike = new CityBike();
 bike.introduceYourSelf("I'm a city bike"); //error - Bicycle does not have method with argument string

The calc methods in B, C, D don't override the calc mehtod in A, so when you invoke calc via a1(It's a A), it actually call the method define in A(public int calc (double num)), so the result is 11.(That is to say, a1 is treated as a A, not a D. And note that there is only one method define in A) B,C,D中的calc方法不会覆盖A中的calc方法,因此,当您通过a1(它是A)调用calc时,它实际上会调用A中的方法define(public int calc(double num)),因此结果为11(也就是说,a1被视为A,而不是D。并且请注意,在A中仅定义了一种方法)

But when you invoke calc via d1, which has defined 4 versions of calc, the result is depends on the type of your argument and the parameter. 但是,当您通过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... 为什么a1引用的对象(具有声明的类型A和运行时类型D)调用在A中声明的方法,而不是最合适的方法...

Actually, it's not the most appropriate one. 实际上,这不是最合适的一种。

Consider Widening primitive conversions which will be used for chosing the most appropriate method. 考虑加宽基元转换 ,该转换将用于选择最合适的方法。

The possible conversions are : 可能的转换为:

  • byte to short, int, long, float, or double 字节到short,int,long,float或double

  • short to int, long, float, or double 短至int,long,float或double

  • char to int, long, float, or double char转换为int,long,float或double

  • int to long, float, or double int转换为long,float或double

  • long to float or double 长时间漂浮或翻倍

  • float to double 浮动到两倍

You expect the method that takes a float as parameter to be chosen. 您期望选择以浮点数作为参数的方法。 However as you can see in the above list, double can never fit in another type than... double. 但是,如您在上面的列表中所见,double永远不能适合于... double。

JLS 15.12.2.5 close the interrogation : 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. 非正式的直觉是,如果第一种方法处理的任何调用都可以传递给另一个方法而没有编译时错误 ,则一个方法比另一种方法更具体

Hence since float can be passed to double and double can't be passed to float, the method from A is chosen as the most appropriate. 因此,由于可以将float传递给double且不能将double传递给float,因此选择A中的方法是最合适的。


Why does it seem to behave so differently from the Bicycle example? 为什么它的行为似乎与Bicycle示例有很大不同?

In the bicycle example, you are overriding methods while in the second example you are overloading. 在自行车示例中,您将覆盖方法,而在第二个示例中,您将重载。 When overloading the most appropriate method (according to JLS 15.12.2.5) while when overriding you will call the "nearest" method of your runtime object. 当重载最合适的方法时(根据JLS 15.12.2.5),而在覆盖时,您将调用运行时对象的“最近”方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM