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

时间:2016-03-22 01:38:12

标签: java polymorphism runtime override overloading

我有以下用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示例有如此不同?感谢。

3 个答案:

答案 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),当覆盖时,你将调用"最近的"运行时对象的方法。