重载和重载方法中的多态性
我们来看看这个简单的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对象引用。
让我们先纠正这个说法。 变量ah
是Animal
类型的引用,语句new Horse()
创建一个Horse
类型的实例并将其分配给Animal
引用。
现在术语已经明确,这种行为是预期的,被称为runtype-polymorphism或动态方法dispatch。 在编译时, eat()
基于类型为Animal
的引用类型进行解析,但在运行时,将调用的方法基于实例类型Horse
。
当我有一个引用对象类型的generics引用变量类型时,我怎么能确定编译器将调用哪个方法
您可以按照以下简单步骤操作:
- 检查被调用的方法。
ah.eat()
正在调用方法eat
。 - 查看父类和子类中是否存在具有完全相同签名的方法(返回类型协方差除外)。 (方法是否覆盖?)
- 检查参考类型。 在
Animal ah = new Horse()
,引用类型是Animal
,它是父类 - 检查实例类型。 在
Animal ah = new Horse()
,实例类型是Horse
,它是子类。
如果满足上述所有条件,则会查看runtype多态,并将调用子类中的方法。 在任何其他场景中,将根据引用类型解析要调用的方法。
理解子类inheritance父类的方法也是值得的。 让我们说你从Horse
类中删除了public void eat()
方法,你不再覆盖 eat()
方法; 然而, Horse
的public void eat(String s)
方法仍被称为从Animal
过载inheritance的eat
方法。 接下来,让我们在Animal
添加一个public void eat(String s)
方法。 通过此添加,您现在正在重载 Animal
的eat
方法并在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的成员。 这就是编译器打印子类方法值的原因。