重载和覆盖方法中的多态性
Polymorphism in Overloaded and Overridden Methods
让我们使用这个简单的 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();
输出是 "Horse eating hay",这又是完全合乎逻辑的。
这就是让我感到困惑的地方。当我输入:
Animal ah = new Horse();
ah.eat();
我得到:
Horse eating hay
我希望编译器从 Animal class 引用调用 eat() 方法,而不是 Horse 对象引用。
所以我的问题是,当我有一个通用引用变量时,我如何才能确定编译器将调用哪个方法
引用对象类型的类型(例如:Animal horse = new Horse();
这在Java中称为动态绑定。使用显式对象类型而不是引用类型。
无法使用单个方法调用覆盖的超级方法和覆盖方法,请参阅:How to call the overridden method of a superclass。你可以给你的马添加一个方法,它将调用委托给动物,比如:
public class Horse extends Animal {
public void animalEat() {
super.eat();
}
public void eat() {
System.out.println("Horse eating hay ");
}
}
好的,
new 关键字将创建给定 class...
的实例
new Horse();
现在 Horse class 已经是 Animal 的子代了。所以下面会被实例化。
public void eat() {
System.out.println("Horse eating hay ");
}
现在您正在尝试将该对象存储在 Animal 的对象中。
这意味着 Object Of Horse 存储在 Animal 的 Object 中。
Animal ah = new Horse();
所以在Animal的对象中已经存储了Horse的成员。
这就是编译器打印子 class 方法值的原因。
I expected the compiler to invoke the eat() method from the Animal class reference, not the Horse object reference.
我们先纠正一下这个说法。变量 ah
是类型 Animal
的引用,语句 new Horse()
创建类型 Horse
的实例并将其分配给 Animal
引用。
现在术语已经清楚了,这种行为是预料之中的,被称为 runtype-polymorphism 或动态方法分派。在编译时,eat()
是根据类型为Animal
的引用类型来解析的,但是在运行时,将要调用的方法是基于实例 类型是 Horse
.
how can I know for sure which method the compiler is going to invoke when I have a generic reference variable types referring to an object type
您可以按照以下简单步骤操作:
- 检查调用的方法。
ah.eat()
正在调用方法 eat
。
- 查看 parent 和 child class 中是否存在具有完全相同签名的方法(return 类型协方差除外)。 (方法是否被覆盖?)
- 检查引用类型。在
Animal ah = new Horse()
中,引用类型是 Animal
即 parent class
- 检查实例类型。在
Animal ah = new Horse()
中,实例类型是 Horse
即 child class.
如果满足上述所有条件,您正在查看 runtype 多态性,并且将调用 child class 中的方法。在任何其他场景下,将根据引用类型解析要调用的方法。
了解 child class 从其 parent 继承方法也是值得的。假设您从 Horse
class 中删除 public void eat()
方法,您不再 Overrding eat()
方法;然而,Horse
中的public void eat(String s)
方法仍然被称为重载从Animal
继承的eat
方法。接下来,让我们在 Animal
中添加一个 public void eat(String s)
方法。通过此添加,您现在 Overloading Animal
中的 eat
方法和 Overrding Horse
中的方法class。无论您如何更改代码,上面提到的 4 个步骤将始终帮助您决定调用哪个方法。
这是由于方法覆盖而发生的。在方法覆盖中,引用类型并不重要,重要的是对象类型。 Animal ah
只是对对象的引用,实际对象的类型为 Horse
。因此,将调用 Horse
的方法而不是引用类型 Animal
的方法。
让我们使用这个简单的 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();
输出是 "Horse eating hay",这又是完全合乎逻辑的。
这就是让我感到困惑的地方。当我输入:
Animal ah = new Horse();
ah.eat();
我得到:
Horse eating hay
我希望编译器从 Animal class 引用调用 eat() 方法,而不是 Horse 对象引用。
所以我的问题是,当我有一个通用引用变量时,我如何才能确定编译器将调用哪个方法 引用对象类型的类型(例如:Animal horse = new Horse();
这在Java中称为动态绑定。使用显式对象类型而不是引用类型。
无法使用单个方法调用覆盖的超级方法和覆盖方法,请参阅:How to call the overridden method of a superclass。你可以给你的马添加一个方法,它将调用委托给动物,比如:
public class Horse extends Animal {
public void animalEat() {
super.eat();
}
public void eat() {
System.out.println("Horse eating hay ");
}
}
好的, new 关键字将创建给定 class...
的实例new Horse();
现在 Horse class 已经是 Animal 的子代了。所以下面会被实例化。
public void eat() {
System.out.println("Horse eating hay ");
}
现在您正在尝试将该对象存储在 Animal 的对象中。 这意味着 Object Of Horse 存储在 Animal 的 Object 中。
Animal ah = new Horse();
所以在Animal的对象中已经存储了Horse的成员。 这就是编译器打印子 class 方法值的原因。
I expected the compiler to invoke the eat() method from the Animal class reference, not the Horse object reference.
我们先纠正一下这个说法。变量 ah
是类型 Animal
的引用,语句 new Horse()
创建类型 Horse
的实例并将其分配给 Animal
引用。
现在术语已经清楚了,这种行为是预料之中的,被称为 runtype-polymorphism 或动态方法分派。在编译时,eat()
是根据类型为Animal
的引用类型来解析的,但是在运行时,将要调用的方法是基于实例 类型是 Horse
.
how can I know for sure which method the compiler is going to invoke when I have a generic reference variable types referring to an object type
您可以按照以下简单步骤操作:
- 检查调用的方法。
ah.eat()
正在调用方法eat
。 - 查看 parent 和 child class 中是否存在具有完全相同签名的方法(return 类型协方差除外)。 (方法是否被覆盖?)
- 检查引用类型。在
Animal ah = new Horse()
中,引用类型是Animal
即 parent class - 检查实例类型。在
Animal ah = new Horse()
中,实例类型是Horse
即 child class.
如果满足上述所有条件,您正在查看 runtype 多态性,并且将调用 child class 中的方法。在任何其他场景下,将根据引用类型解析要调用的方法。
了解 child class 从其 parent 继承方法也是值得的。假设您从 Horse
class 中删除 public void eat()
方法,您不再 Overrding eat()
方法;然而,Horse
中的public void eat(String s)
方法仍然被称为重载从Animal
继承的eat
方法。接下来,让我们在 Animal
中添加一个 public void eat(String s)
方法。通过此添加,您现在 Overloading Animal
中的 eat
方法和 Overrding Horse
中的方法class。无论您如何更改代码,上面提到的 4 个步骤将始终帮助您决定调用哪个方法。
这是由于方法覆盖而发生的。在方法覆盖中,引用类型并不重要,重要的是对象类型。 Animal ah
只是对对象的引用,实际对象的类型为 Horse
。因此,将调用 Horse
的方法而不是引用类型 Animal
的方法。