首先,澄清一下术语:我们将一个Child
对象分配给一个类型为 的变量Parent
。Parent
是对碰巧是 a 的子类型的对象的Parent
引用Child
。
它仅在更复杂的示例中有用。想象一下,您添加getEmployeeDetails
到类 Parent:
public String getEmployeeDetails() {
return "Name: " + name;
}
我们可以重写该方法Child
以提供更多详细信息:
@Override
public String getEmployeeDetails() {
return "Name: " + name + " Salary: " + salary;
}
现在您可以编写一行代码来获取任何可用的详细信息,无论对象是 aParent
还是Child
:
parent.getEmployeeDetails();
以下代码:
Parent parent = new Parent();
parent.name = 1;
Child child = new Child();
child.name = 2;
child.salary = 2000;
Parent[] employees = new Parent[] { parent, child };
for (Parent employee : employees) {
employee.getEmployeeDetails();
}
将导致输出:
Name: 1
Name: 2 Salary: 2000
我们使用 aChild
作为Parent
. 它具有该类独有的特殊行为Child
,但是当我们调用时,getEmployeeDetails()
我们可以忽略差异并专注于如何Parent
和Child
相似。这称为子类型多态性。
您更新的问题询问为什么当对象存储在引用中Child.salary
时无法访问。答案是“多态性”和“静态类型”的交集。因为 Java 在编译时是静态类型的,所以您可以从编译器中获得某些保证,但您不得不遵守规则作为交换,否则代码将无法编译。在这里,相关保证是子类型(例如)的每个实例都可以用作其超类型(例如)的实例。例如,您可以保证当您访问或方法或字段定义在任何可以分配给类型变量的非空对象上时Child
Parent
Child
Parent
employee.getEmployeeDetails
employee.name
employee
Parent
. 为了保证这一点,编译器Parent
在决定您可以访问什么时只考虑该静态类型(基本上是变量引用的类型)。因此,您无法访问在对象的运行时类型上定义的任何成员,Child
.
当您真正想将 aChild
用作 a时,Parent
这是一个易于使用的限制,您的代码将可用于Parent
及其所有子类型。如果这不可接受,请指定引用的类型Child
。