繁体   English   中英

为什么这种类型的继承有效?

[英]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类型的生物。 这是允许的,因为PersonLiving的专业化。

但是我仍然无法通过点运算符访问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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM