重载和重载方法中的多态性

我们来看看这个简单的Java代码:

public class Animal { public void eat() { System.out.println("Generic Animal Eating Generically"); } } public class Horse extends Animal { public void eat() { System.out.println("Horse eating hay "); } public void eat(String s) { System.out.println("Horse eating " + s); } } 

我试图找出三个eat()方法的哪个版本将运行。 现在,当我打字

 Animal a = new Animal(); a.eat(); 

输出是“Generic Animal Eating Generically”,这是完全可以理解的。

输入时会发生同样的事情:

 Horse h = new Horse(); h.eat(); 

输出是“马吃干草”,这也是完全合乎逻辑的。

不过这里让我感到困惑。 当我输入:

 Animal ah = new Horse(); ah.eat(); 

我明白了:

 Horse eating hay 

我希望编译器从Animal类引用调用eat()方法,而不是Horse对象引用。

所以我的问题是,当我有一个引用对象类型的generics引用变量类型时,我怎么能确定编译器将调用哪个方法(如下所示:Animal horse = new Horse();

我希望编译器从Animal类引用调用eat()方法,而不是Horse对象引用。

让我们先纠正这个说法。 变量ahAnimal类型的引用,语句new Horse()创建一个Horse类型的实例并将其分配给Animal引用。

现在术语已经明确,这种行为是预期的,被称为runtype-polymorphism或动态方法dispatch。 在编译时, eat()基于类型为Animal的引用类型进行解析,但在运行时,将调用的方法基于实例类型Horse

当我有一个引用对象类型的generics引用变量类型时,我怎么能确定编译器将调用哪个方法

您可以按照以下简单步骤操作:

  1. 检查被调用的方法。 ah.eat()正在调用方法eat
  2. 查看父类和子类中是否存在具有完全相同签名的方法(返回类型协方差除外)。 (方法是否覆盖?)
  3. 检查参考类型。 在Animal ah = new Horse() ,引用类型是Animal ,它是父类
  4. 检查实例类型。 在Animal ah = new Horse() ,实例类型是Horse ,它是子类。

如果满足上述所有条件,则会查看runtype多态,并将调用子类中的方法。 在任何其他场景中,将根据引用类型解析要调用的方法。

理解子类inheritance父类的方法也是值得的。 让我们说你从Horse类中删除了public void eat()方法,你不再覆盖 eat()方法; 然而, Horsepublic void eat(String s)方法仍被称为从Animal 过载inheritance的eat方法。 接下来,让我们在Animal添加一个public void eat(String s)方法。 通过此添加,您现在正在重载 Animaleat方法并在Horse类中覆盖它。 无论您如何更改代码,上述4个步骤将始终帮助您确定将调用哪个方法。

这在Java中称为动态绑定。 使用explicite对象类型而不是引用类型。

无法使用单个方法调用overriden super方法和覆盖方法,请参阅: 如何调用超类的重写方法 。 你可以为你的马添加一个方法,它将调用委托给动物:

 public class Horse extends Animal { public void animalEat() { super.eat(); } public void eat() { System.out.println("Horse eating hay "); } } 

这是因为方法覆盖而发生的。 在方法重写中,引用类型无关紧要,重要的是对象类型。 Animal ah只是对象的引用,实际对象是Horse类型。 因此,将调用Horse的方法而不是引用类型Animal的方法。

哦,新关键字将创建给定类的实例…

 new Horse(); 

现在马类已经是动物的孩子了。 所以下面将实例化。

 public void eat() { System.out.println("Horse eating hay "); } 

现在您尝试将该对象存储在Animal的对象中。 这意味着Object Of Horse存储在Animal的Object中。

 Animal ah = new Horse(); 

所以在Animal的对象中,已经存储了Horse的成员。 这就是编译器打印子类方法值的原因。