Chapter 5 - Java for Beginners Course

Inheritance - Methods and Fields

In the last 2 sections we covered how to define inheritance relationships between classes and also how inheritance relates to the constructors of a class and the order of construction of objects.

In this section we’ll cover how inheritance affects fields and methods between a subclass and its superclass(es). In specific, we’ll cover the super keyword, method overriding and field hiding.

For this section assume we’re building a system for a Toy store and we’ll use the following two classes to introduce the concepts:

public class Toy {
    private String name;

    int fromAge;

    public Toy(String name, int fromAge) {
        this.name = name;
        this.fromAge = fromAge;
    }

    public void printDescription() {
        System.out.println("Toy: " + name + " can be used from age: " + fromAge);
    }

    public void printFromAgeDetails() {
        System.out.println("The minimum age for this toy is: " + fromAge + " years");
    }
}

The above Toy class will be used as our superclass and contains the name of the toy and a fromAge that indicates the minimum age the toy can be used from.

The Toy class above exposes the fromAge field with default access instead of being a private field. This is not recommended and only done to illustrate some concepts in this section.

We’ll now define a subclass RemoteControlCar of our Toy class:

public class RemoteControlCar extends Toy {

    int fromAge;

    public RemoteControlCar() {
        super("Remote Control Car", 10);
        fromAge = 10;
    }

    @Override
    public void printDescription() {
        System.out.println("*** RemoteControlCar class ***");
        System.out.println("This is a remote control car and is suitable from " + fromAge + " years");
    }

}

Method Overriding

In the example classes above, you’ll note that we provided a printDescription() method in both classes with the same signature. When this occurs, we’re telling Java that we want our subclass method to override the original method provided in the superclass.

By overriding the method in our subclass, Java will invoke the overriden method instead of the original one in the superclass.

Let’s give this a try:

In the example code run the JavaInheritanceAndMethodsApp
Toy toyBoat = new Toy("Boat", 1);
toyBoat.printDescription();

System.out.println("--------------------------");

RemoteControlCar toyCar = new RemoteControlCar();
toyCar.printDescription();

Output:

*** Toy class ***
Toy: Boat can be used from age: 1
--------------------------
*** RemoteControlCar class ***
This is a remote control car and is suitable from 10 years

Method Overriding - Example explained

In this case we are creating 2 different objects, toyBoat of type Toy and toyCar of type RemoteControlCar.

When we invoke toyBoat.printDescription() Java uses the printDescription method defined in the Toy class. In the hierarchy of the Toy class (ToyObject) there is no other printDescription() method defined.

In this case, we get the following 2 lines in our output:

*** Toy class ***
Toy: Boat can be used from age: 1

Now, when we call toyCar.printDescription(), the toyCar hierarchy RemoteControlCarToyObject has 2 valid methods that match the signature printDescription(): the original method in the Toy class and an overridden version in RemoteControlCar.

In this case, Java will invoke the first overridden version of the method it finds in the hierarchy, in this case, the one in the RemoteControlCar class, hence why we get:

*** RemoteControlCar class ***
This is a remote control car and is suitable from 10 years

in the output.

Try removing the printDescription() method in the RemoteControlCar class and run the program again. What result do you get?

Invoking the original method in the superclass

From a subclass, we can invoke the original method provided in a superclass. This can be achieved by using the super keyword in Java that allows us to access methods (overridden or not) and fields from a subclass.

To illustrate this, let’s assume we have a new class in our hierarchy called WifiRemoteControlCar that extends our RemoteControlCar class as follows:

public class WifiRemoteControlCar extends RemoteControlCar {

    @Override
    public void printDescription() {
        System.out.println("This RemoteControlCar can be controlled via WiFi");
        super.printDescription();
    }

}

And let’s create an instance and invoke that method:

WifiRemoteControlCar wifiCar = new WifiRemoteControlCar();
wifiCar.printDescription();

Output:

This RemoteControlCar can be controlled via WiFi
*** RemoteControlCar class ***
This is a remote control car and is suitable from 10 years

In this example, when we invoke wifiCar.printDescription() we already know that Java will invoke the overridden method defined in our new class.

Inside that method, we added a call to super.printDescription() and in this case Java will invoke the first printDescription() method in the class' hierarchy starting from the superclass.

For the WifiRemoteControlCar, the hierarchy is WifiRemoteControlCarRemoteControlCarToyObject, and the first printDescription() method Java finds starting from the superclass is in RemoteControlCar.

Hence we get the output of our RemoteControlCar method as well in the output.

Accessing fields from the superclass

As mentioned above, the super class also provides us access to the fields of the superclass. Let’s add the following method to our WifiRemoteControlCar class:

public class WifiRemoteControlCar extends RemoteControlCar {
    //...

    public void printWifiCarFromAge() {
        int minAge = super.fromAge;
        System.out.println("This wifi car is suitable from age: " + minAge);
    }

}

In this case, we are accessing the fromAge field defined in our RemoteControlCar class using the super.fromAge statement, and we get the following result if we invoke it:

WifiRemoteControlCar wifiCar = new WifiRemoteControlCar();
wifiCar.printWifiCarFromAge();

Output:

This wifi car is suitable from age: 10

Is super required?

In the example above we used super.fromAge to tell Java that we want to access the fromAge field defined from a superclass. However, in the WifiRemoteControlCar class there is no other fromAge variable or field defined anywhere.

As there is no chance for ambiguity, Java can safely infer that the fromAge field comes from the superclass (as it is the only one available). In cases like this, there is no need to use the super keyword, and in this case super.fromAge and fromAge refer to the same field.

The same principle applies to method invocations, as long as there is no ambiguity the super keyword is optional.

Field hiding

The last topic to cover in this section is field hiding. You’ll have noticed that in both the RemoteControlCar class and the Toy class we defined an int fromAge field.

When this occurs, the fromAge field from the Toy class is said to be hidden.

Field hiding is not recommended and is a code smell.

In practice, when we have field hiding, the instance of an object ends up with 2 states/fields with the same name. In our case, an instance of RemoteControlCar would have a value in the fromAge field that exists from the RemoteControlCar class and a different fromAge value that exists from the Toy class.

Let’s take a look at this with an example:

In the example code run the JavaFieldHidingApp
RemoteControlCar car = new RemoteControlCar();

// we can change the fromAge value defined in the RemoteControlCar class
car.fromAge = 15;
// and as expected the fromAge in the message below should be 15
System.out.println("The min age for the RemoteControlCar is: " + car.fromAge);

// however, we don't have access to the fromAge field of the Toy car (it is hidden),
// unless of course, we use a method that provides us access to its value, like the
// Toy.printFromAgeDetails() method we defined
car.printFromAgeDetails();

Output:

The min age for the RemoteControlCar is: 15
The minimum age for this toy is: 10 years

In the example above, you can see we have 2 different values as we have 2 separate fromAge fields, one in each class. As mentioned before, field hiding is not recommended as it becomes confusing and the code becomes more difficult to read.

The example above is to illustrate the concept only.
Try removing the fromAge field in the RemoteControlCar class and run the program again. What result do you get?

A note on @Override

If you’re asking yourself about what the @Override is on top of our overridden methods, we’ll cover them in a later chapter, but for now:

  • When you see @ symbols, these are called annotations in Java. So, @Override can be read as the Override annotation.

  • Annotations are a form of metadata we provide in our code.

  • The @Override annotation in particular on top of a method tells the Java compiler that this is an overridden method.