简体   繁体   中英

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. This is allowed because Person is a specialization of Living .

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 .

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. 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. If creature does not have a name attribute and its toString() method is not overriden, how can I get the above output?

The object is indeed an instance of a Person , and thus has all of the Person methods. However, the reference is of the Living type. 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.

It could, however, be coerced to treat this variable as a Person , but explicit casting:

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() 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.).

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 .

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 :

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"); you are defining a variable which type is Living , but as you stated, specialized as Person . That means it won't have Person methods, only its specializations!

So, for example, if Living had a method

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

and you called creature.getName() , the return would NOT be "foobar" , but the "Joe" instead. 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.

The object is of type Person , but the reference if of type Living . As Living doesn't declare a getName() method, you get a compilation error, when you try to call it.

The toString() method is actually declared in Object , which is an implicit parent of Living , so toString() is declared. As the object is of type Person , and it overrides toString() , it is the one called.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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