首先,要澄清术语:我们正在将Child
对象分配给type变量Parent
。Parent
是正好是的子类型的对象的引用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
我们将Child
用作Parent
。它具有Child
班级特有的特殊行为,但是当我们打电话时,getEmployeeDetails()
我们可以忽略差异,而专注于方式Parent
和Child
相似之处。这称为亚型多态性。
您更新的问题询问为什么Child.salary
将Child
对象存储在Parent
引用中时无法访问。答案是“多态性”和“静态类型”的交集。因为Java在编译时是静态类型的,所以您会从编译器获得某些保证,但是您必须遵循规则进行交换,否则代码将无法编译。在此,相关保证是,子类型(例如Child
)的每个实例都可以用作其超类型(例如Parent
)的实例。例如,可以保证在访问时employee.getEmployeeDetails
或employee.name
在可以分配给employee
类型变量的任何非空对象上定义方法或字段时Parent
。为了保证这一点,编译器Parent
在决定您可以访问的内容时仅考虑该静态类型(基本上是变量引用的类型)。因此,您无法访问在对象的运行时类型上定义的任何成员Child
。
当您真正想使用a时Child
,Parent
这是一个易于使用的限制,您的代码将可用Parent
于其所有子类型。如果不可接受,请创建引用的类型Child
。