简体   繁体   English

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

[英]Why does this type of inheritance work?

First I define the superclass: 首先我定义超类:

public class Living {
    double energy;

    Living(double energy) {
        this.energy = energy;
    }
}

Then I define the subclass: 然后我定义子类:

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;
    }

}

Now if I do this: 现在,如果我这样做:

Living creature = new Person(15.2, "Joe");

I will have created a creature of type Living by using the Person constructor. 我将使用Person构造函数创建一个Living类型的生物。 This is allowed because Person is a specialization of Living . 这是允许的,因为PersonLiving的专业化。

However I still cannot access the methods of the Person class - for example creature.getName() - through the point operator, because creature is declared as of type Living not Person . 但是我仍然无法通过点运算符访问Person类的方法 - 例如creature.getName() - 因为creature被声明为Living not Person类型。

System.out.println(creature.getName()); //not possible

But if I do this: 但如果我这样做:

System.out.println(creature.toString());

I get 我明白了

Joe 15.2

So instead of the toString() method of the Living class (which is not overriden), the toString() method of the Person class is called. 因此,不是Living类的toString()方法(不覆盖),而是调用Person类的toString()方法。 I am having trouble understanding why this is the case. 我无法理解为什么会这样。

I declare in the beginning that creature should be of type Living , although I use the Person constructor. 我在开头声明生物应该是Living类型,尽管我使用Person构造函数。 If creature does not have a name attribute and its toString() method is not overriden, how can I get the above output? 如果creature没有name属性且其toString()方法未被覆盖,我该如何获得上述输出?

The object is indeed an instance of a Person , and thus has all of the Person methods. 该对象确实是Person一个实例,因此具有所有Person方法。 However, the reference is of the Living type. 但是,参考文献是Living类型。 The compiler does not know its runtime type, and only allows you to access methods it "knows" will be available - ie, the methods of the Living type. 编译器不知道它的运行时类型,只允许您访问它“知道”可用的方法 - 即Living类型的方法。

It could, however, be coerced to treat this variable as a Person , but explicit casting: 但是,它可以被强制将此变量视为Person ,但显式转换:

System.out.println(((Person) creature).getName())

For every object, there is a table (virtual table) which keeps information about the methods defined for this object. 对于每个对象,都有一个表(虚拟表),用于保存有关为此对象定义的方法的信息。

If a parent method is overridden in a given object, this table will store the address of the overridden method, even if the object is referenced as one of the parent class. 如果在给定对象中覆盖父方法,则此表将存储重写方法的地址,即使该对象被引用为父类之一也是如此。 Every time a method is called, this table is used to find out how to access that method. 每次调用方法时,此表用于查找如何访问该方法。

Therefore, in your case it will use the redefined method toString, because it has replaced the parent method. 因此,在您的情况下,它将使用重新定义的方法toString,因为它已替换父方法。

toString() is declared in Object (from which all your classes inherit), that's why you can call it without being overridden in your class Living (some goes for methods such as equals() , hashCode() , etc.). toString()Object中声明(所有类都继承自),这就是为什么你可以调用它而不在类Living重写(有些用于equals()hashCode()等方法)。

Notice, that if you would call the toString() on a different subclass of Living , such as Animal , which does not explicitly override toString() , you would get a default toString() result from Object . 注意,如果你在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

}

Calling toString() on Animal : Animal上调用toString()

Living animal = new Animal(100, "Fido");
System.out.println(animal.toString()); // something like your.package.Animal@757dbeaf

That's one of the main characteristics of Object-oriented programming. 这是面向对象编程的主要特征之一。

When you do Living creature = new Person(15.2, "Joe"); 当你做Living creature = new Person(15.2, "Joe"); you are defining a variable which type is Living , but as you stated, specialized as Person . 你正在定义一个变量,其类型是Living ,但正如你所说,专门作为Person That means it won't have Person methods, only its specializations! 这意味着它不会有Person方法,只有它的专业化!

So, for example, if Living had a method 所以,例如,如果Living有一个方法

public String getName() {
    return "foobar";
}

and you called creature.getName() , the return would NOT be "foobar" , but the "Joe" instead. 你调用了creature.getName() ,返回的不是"foobar" ,而是"Joe" Because the 'specialization' of the method will be called, and not the super method itself. 因为将调用方法的“特化”,而不是超级方法本身。

However, only methods that are 'specialized' can be called: those who are particular to Person or any other subclass can't. 但是,只能调用“专用”的方法:特定于Person或任何其他子类的方法不能。

The object is of type Person , but the reference if of type Living . 对象的类型为Person ,但类型为Living的引用。 As Living doesn't declare a getName() method, you get a compilation error, when you try to call it. 由于Living未声明getName()方法,因此在尝试调用时会出现编译错误。

The toString() method is actually declared in Object , which is an implicit parent of Living , so toString() is declared. 所述toString()方法在实际声明Object ,这是一个隐式的母体Living ,所以toString() 声明。 As the object is of type Person , and it overrides toString() , it is the one called. 由于对象是Person类型,并且它覆盖toString() ,因此它是被调用的对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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