[英]Why does this type of inheritance work?
首先我定义超类:
public class Living {
double energy;
Living(double energy) {
this.energy = energy;
}
}
然后我定义子类:
public class Person extends Living {
String name;
Person(double energy, String name) {
super(energy);
this.name = name;
}
public String getName() {
return this.name;
}
public String toString() {
String s = this.getName() + " " + this.energy;
return s;
}
}
现在,如果我这样做:
Living creature = new Person(15.2, "Joe");
我将使用Person
构造函数创建一个Living
类型的生物。 这是允许的,因为Person
是Living
的专业化。
但是我仍然无法通过点运算符访问Person
类的方法 - 例如creature.getName()
- 因为creature被声明为Living
not Person
类型。
System.out.println(creature.getName()); //not possible
但如果我这样做:
System.out.println(creature.toString());
我明白了
Joe 15.2
因此,不是Living
类的toString()
方法(不覆盖),而是调用Person
类的toString()
方法。 我无法理解为什么会这样。
我在开头声明生物应该是Living
类型,尽管我使用Person
构造函数。 如果creature没有name
属性且其toString()
方法未被覆盖,我该如何获得上述输出?
该对象确实是Person
一个实例,因此具有所有Person
方法。 但是,参考文献是Living
类型。 编译器不知道它的运行时类型,只允许您访问它“知道”可用的方法 - 即Living
类型的方法。
但是,它可以被强制将此变量视为Person
,但显式转换:
System.out.println(((Person) creature).getName())
对于每个对象,都有一个表(虚拟表),用于保存有关为此对象定义的方法的信息。
如果在给定对象中覆盖父方法,则此表将存储重写方法的地址,即使该对象被引用为父类之一也是如此。 每次调用方法时,此表用于查找如何访问该方法。
因此,在您的情况下,它将使用重新定义的方法toString,因为它已替换父方法。
toString()
在Object中声明(所有类都继承自),这就是为什么你可以调用它而不在类Living
重写(有些用于equals()
, hashCode()
等方法)。
注意,如果你在Living
的另一个子类上调用toString()
,比如Animal
,它没有显式覆盖toString()
,你将从Object
得到一个默认的toString()
结果。
public class Animal extends Living {
String name;
Person(double energy, String name) {
super(energy);
this.name = name;
}
public String getName() {
return this.name;
}
// no toString override here
}
在Animal
上调用toString()
:
Living animal = new Animal(100, "Fido");
System.out.println(animal.toString()); // something like your.package.Animal@757dbeaf
这是面向对象编程的主要特征之一。
当你做Living creature = new Person(15.2, "Joe");
你正在定义一个变量,其类型是Living
,但正如你所说,专门作为Person
。 这意味着它不会有Person
方法,只有它的专业化!
所以,例如,如果Living
有一个方法
public String getName() {
return "foobar";
}
你调用了creature.getName()
,返回的不是"foobar"
,而是"Joe"
。 因为将调用方法的“特化”,而不是超级方法本身。
但是,只能调用“专用”的方法:特定于Person
或任何其他子类的方法不能。
对象的类型为Person
,但类型为Living
的引用。 由于Living
未声明getName()
方法,因此在尝试调用时会出现编译错误。
所述toString()
方法在实际声明Object
,这是一个隐式的母体Living
,所以toString()
被声明。 由于对象是Person
类型,并且它覆盖toString()
,因此它是被调用的对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.