RobotWorld logo

Basic Inheritance

The definition of a class from the last chapter is

An instance of a subclass is partly an instance of the superclass in that it has all the instance variables that the superclass has, and it has all the methods of the superclass. The subclass can replace or extend the methods of the superclass, but it can do nothing about the instance variables. And, as stated before, the subclass can also add instance variables and new methods that the superclass doesn't have.

Pet

Let's start building a simple example to explore the ideas of inheritance. First we will create a class called Pet which will store the name and age of a particular pet. Pet will be the superclass for all of the derived classes discussed later in this section.

public class Pet {
    public Pet(String n, int a) {
        name = n;
        age = a;
    }

    public void describe() {
        System.out.println("I have a pet named " + name + ".");
        System.out.println(name + " is " + age + " years old.");
    }

    public void talk() {
        System.out.println("Meaningless noise");
    }

    private String name;
    private int age;
}

There is nothing special about this superclass, it is a class just like any other class, in fact, this is very much like our Person example.

Suppose we run the following code

Pet yogi = new Pet("Yogi", 1);
yogi.talk();
yogi.describe();

Here is the instance of Pet that is created.

And, as expected, the output of the code snippet will be

Meaningless noise
I have a pet named Yogi.
Yogi is 1 years old.

Nothing new here.

Class Fish

Now let's make a sublcass of Pet called Fish.

public class Fish extends Pet {
    public Fish(String n, int a) {
        super(n, a);
    }

    public void talk() {
        System.out.println("Glub, glub");
    }
}

The keyword extends says that Pet is the superclass of Fish. Another way to phrase this is that Fish is a subclass, or a derived class, of Pet.

Suppose we run the following code

Fish fish = new Fish("Eric", 2);
fish.talk();

As expected, the output will be

Glub, glub

Here is the instance of Fish that is created. Notice that Fish adds no instance variables of its own, but, as was said above, it does get all the instance variables from Pet.

Don't worry just yet about what super means in the constructor for Fish.

Class Cat

Here is another subclass of Pet called Cat. Notice the use of extends again.

public class Cat extends Pet {
    public Cat(String n, int a) {
        super(n, a);
    }

    public void talk() {
        System.out.println("Meow");
    }

    public void sharpenClaws() {
        System.out.println("Which couch is next, I wonder???");
    }
}

Suppose we run the following code

Cat c = new Cat("Emily", 7);
c.talk();
c.sharpenClaws();

Like Fish, Cat adds no new instance variables of its own, but it inherits all the instance variables from Pet. Here is the instance of Cat created in the code above.

Notice that the class Cat has an extra method called sharpenClaws() that isn't found in the Pet class. Cat will have all the methods in Pet and this new one.

The output of the code fragment above will be

Meow
Which couch is next, I wonder???

Still wondering about what the super keyword means in the constructor for Cat? Soon!

Class Ferret

And here is one more class derived from Pet called Ferret.

public class Ferret extends Pet {
    public Ferret(String n, int a) {
        super(n, a);
    }

    private String teeth = "Sharp";
}

Unlike the last two subclasses, Ferret does add an instance variable of its own called teeth. It still gets all of the instance variables of Pet, it just adds one of it's own. Here is a sample instance of Ferret.

Notice that no new methods have been added to Ferret.

Method Inheritence

Now let's see how method inheritence works. Here is a main driver which creates instances of our various subclasses and calls methods talk() and describe() on them.

public static void main(String[] args) {
    Cat emily = new Cat("Emily", 1);
    Fish eric = new Fish("Eric", 2);
    Ferret entropy = new Ferret("Entropy", 5);

    emily.talk();
    eric.talk();
    emily.describe();
    entropy.talk();
}

Here is the output from the above code.

Meow
Glub, glub
I have a pet named Emily.
Emily is 1 years old.
Meaningless noise.

Let's try and understand what we're seeing.

Our first method call is

emily.talk();

emily is of type Cat. We look in the class definition for Cat and notice that there is a method called talk() which has no arguments. So we use the method talk() found in Cat. It doesn't matter that there is a definition of talk() in Pet as we found one in Cat and we use the first one we find. So Emily says

Meow

Now let's look at the next line.

eric.talk();

eric is of type Fish. We look in the class definition for Fish and notice that there is a method called talk(). So we use the method talk() found in Fish. This is the same thing as happend with emily.talk() except that eric is an instance of class Fish so we had to start looking in class Fish for the method. So Eric says

Glub, glub

Now let's look at something a little more involved.

emily.describe();

emily is of class Cat, so we start looking in Cat for the definition of describe(). We don't find a method called describe() in Cat. What we now have to do is look for the method in the superclass for Cat. So we look at the class definition for Cat to the right of the extends keyword. We see that the superclass for Cat is Pet. Since we didn't find describe() in Cat, we will now have to look for it in Pet since Pet is the superclass of Cat. This is what method inheritence means.

And sure enough, we find a method called describe() in Pet.

I have a pet named Emily.
Emily is 1 years old.

If we hadn't found a describe() in Pet, we would had to look in its superclass. And if it wasn't in Pets superclass we'd have to look in that classes superclass and on up the chain. If we never find such a method and we end up in a class without a superclass we would have a syntax error.

For instance

emily.scoobyDoo();

would be a syntax error because scoobyDoo() isn't defined either in class Cat or class Pet. Pet has no superclass, so we have to stop looking there.

One more example.

entropy.talk();

This works the same way as describe() did when we called it on emily. entropy is an instance of class Ferret, so we look in Ferret for the definition of talk(). There isn't a method called talk() in Ferret so what we now have to do is look for the method in the superclass for Ferret. We look at the class definition for Ferret to the right of the extends keyword and see that the superclass for Ferret is Pet. Since we didn't find talk() in Ferret, we will now have to look for it in Pet since Pet is the superclass of Ferret. We find a method with the correct signature and generate the output

Meaningless noise.

When we called emily.talk(), we only used the method called talk() in Cat even though there was a method called talk() in Pet. This is because we started looking for talk() in Cat and we use the first one we find and ignore any others after that.

Ferret didn't have a method named talk() of its own, so we had to go look in its superclasses.

We say that the talk() in Cat overrides the talk() in Pet. Why is this useful? It means we can put some reasonable default behaviour in the method in the superclass and use the subclasses a specific behaviour more appropriate to the subclass. Cat is the subclass and cats say "meow" when they talk. An argument can be made that "Meaningless noise" is a reasonable default if we don't know the noise the pet makes, so that is the one that we put in the superclass.

Basic Inheritance Summary

Inheritance in Java involves a superclass and a subclass, sometimes called a derived class. The subclass uses the extends keyword in its class definition to say what the superclass is.

Java implements what is called a single-inheritance model. What this means to you is that a subclass in Java can only extend one class.

The superclass of a class might also be a subclass of yet another superclass, you will see an example of this in the next section. We call this the class chain for a class, which is the superclass of the class, the superclass' superclass, and on up until there are no more superclasses.

A class in Java is a collection of instance variables and instance methods. When a class inherits from a superclass it gets all of the instance variables and all of the instance methods from the superclass, which will include any instance variables or instance methods from the superclass of the superclass, all the way up the class chain. You saw an example of this with the class Ferret, which had an instance variable of its own, but also inherited the instance variables from the class Pet.

Finding out which instance method to call has now been extended. As stated before, there must be a method with the same number of arguments with the same types in the same order for Java to know which method to call. It starts by looking for the method in the class type of the class instance. If it finds the method in that class, that method will be called. This happened above when we evaluated the statement emily.talk(). If the class doesn't contain a method which matches the method signature of the method call being made the search will continue with the class' superclass. If the class doesn't have a superclass, there is a suntax error because no method could be found. If there is a superclass, its methods will be examined for one with the proper signtature. We saw an example of this above when we evaluated entropy.talk(). Its class Ferret did not define talk(), but its superclass did.

This search will continue up the entire superclass chain until either a matching method is found or there are no more superclasses. If we run out of superclasses and have found no matching method, it is a syntax error.