Inheritance
Code reuse is probable one of the most powerful features of an object oriented programming language. While most programming languages provide functions / procedures which provide a low-level construct for code reuse, object oriented programming languages take this an important step further. OOP languages allow us to define classes and create relations between these classes, facilitating not only code reuse but also better overall design.
Inheritance is one of the main pillars of an object-oriented programming language and provides a clean way of reusing functionality. It allows a class to inherit both the attributes and methods of another class. Common data and functionality is structured inside a base class (also called superclass) from which the more specific classes inherit (called subclasses or derived classes) properties and behavior.
WARNING - Copy paste is not reuse
For all you copy-paste fanatics out there, copying code from one place to another is not considered code reuse. If the original code is ever extended, changed or corrected, chances are that you will forget to alter one of the copies.
As with most in an OOP language, the solution revolves around the class. Instead of creating classes from scratch we can use existing ones, extend them or embed them within our classes. The main advantages of this approach is that:
we reuse code that has been tested and debugged
code is not duplicated
classes can be kept small and thus more manageable
There are actually three ways to accomplish this and two have already been discussed in this course.
The first approach is to create objects of existing classes and use these within our code. These classes can be pre-made, part of a library or even the ones we create.
The second approach is to create and embed objects of the already existing class inside the class you are developing. This is called composition as the new class is composed of objects of existing classes.
The third approach is to create a new class as a subtype of an existing class. You literally take the form of an existing class and extend it, and this without modifying the code of the existing class. This is called Inheritance.
These three approaches are important cornerstones of an object oriented programming language.
Introducing Inheritance
Inheritance allows a class to inherit (get) the properties and methods of another class. In other words, the subclass inherits the state and behavior from the base class. The subclass is also called the derived class while the base class is also known as the super-class. The derived classes can add their own additional attributes and methods. These additional attributes and methods differentiate the derived classes from the base class.
A super-class can have any number of subclasses. In Java a subclass can have only one superclass.
Vehicle Example
Consider a first example that models the classes Truck
, Racecar
and Bus
. Each have a number of attributes and some methods. Below is a partial UML class diagram of the classes modeled without the use of inheritance. Each class has the full functionality implemented into the class itself.

When taking a look at this example it is pretty obvious that the design includes a lot of duplication. This can be solved by creating a superclass Vehicle
that contains all the common attributes and methods of these classes.

INFO - Inheritance in UML class diagram
In a UML class diagram inheritance is depicted using a solid line and a hollow arrow attached at the side of the superclass.
While not documented inside the UML class diagrams, the subclasses Truck
, Racecar
and Bus
also contain the attributes and methods of Vehicle
.
Some attributes (such as the trailer
of a Truck
) and methods (such as board(passenger)
of Bus
) cannot be generalized to the Vehicle
class. They are specific to the subclasses. This is perfectly valid when implementing inheritance.
By placing the common functionality and data inside a superclass a cleaner design is realized that also encourages code reuse.
Computer Store Example
Let us revise the following application that is being build for an online computer web shop. A beginning developer has modeled some of the items that his client wants to sell online. However he has come to the conclusion that his design needs to be refactored.

In a first iteration the most common attributes are extracted to a superclass. A good name might be Product
. Since it is a store that sells products, it seems a logical choice. When selecting attributes from the subclasses to be placed inside Product
, we must ask our self the question if that attribute is a logical property of a general computer store product. If not, then something is wrong or our models are wrong.

Code always changes
Something to remember is that code evolves. It changes over time as things get added, removed or refactored. Static code will eventually become outdated and die. On top of that your boss, client, teacher, ... will never tell you the whole story. Once they get the first prototype, and they like it, there will always be a "would it be possible to add ..." moment. That is also why it is also more fun to program based on methodologies such as SCRUM and Agile as they take the fact of change into account.
So let us take the computer store example. Our developer needs help again as his client asked him to also add games to the list of products to sell. The overeager developer created a new class Game
that inherits from the Product
class. At first sight nothing seems wrong with it.

First of all it needs to be noted that adding a new product class was really easy as a lot of the functionality and properties are inherited from the Product
base class.
Now taking a closer look at the classes, something can be noticed. Does a game have a model
or a brand
? In real life: no. Games have a publisher
and developers
. Than why does it have a model and brand in the application?
Actually the class Product
is not entirely accurate after games came into play. While each computer hardware product does have a model and brand, software and games do not. Basically we need to add a class Hardware
which inherits from the Product
class. Then our hardware products can inherit from Hardware
and implicitly also from Product
, while Game
directly inherits from Product
.

Is-a Relationships
The superclass and subclass have an "is-a" relationship between them. Take the basic example of pets shown below.

Here we can for example state that a Cat
is-a Pet
, a Bunny
is-a Pet
and a Dog
is-a Pet
.
If you cannot logically state that 'subclass' is-a 'superclass' than you made a mistake to make 'subclass' inherit from 'superclass'. An example of this would be the case when you would create a subclass Mosquito
from Pet
because Mosquito
also has color, favorite food and an age. This may seem DRY but it is illogical. You can't state that Mosquito
is-a Pet
.
If we needed to model both a Bus
class and a Car
class it makes perfect sense to create a Vehicle
class and make both Bus
and Car
inherit from them. It's perfectly valid to state that
a
Bus
is aVehicle
a
Car
is aVehicle
However it would of been illogical to make Bus
inherit from Car
or vice versa as it would not have been logical to state that:
a Bus is a Car
a Car is a Bus
A ChoppingCart
should not inherit from Vehicle
because it has wheels.
Inheritance in Java
To implement inheritance in Java all you need is a base class and a subclass. The subclass needs to extend the base class and this can be accomplished by using the extends
keyword as shown below in the code template.
Note that extending the base class is exactly what we are doing when implementing inheritance. We take a general class and add something to it: data, behavior or both.
Depending on the context and strategy, inheritance can also be though of as generalization, where functionality of subclasses is extracted and placed inside a more generalized super class.
Pets
Below is an example of a class Cat
that inherits from a class Pet
, a baseclass for all sorts of possible pets. A Pet
has an age
and a name
. Each class inheriting from Pet
will also have these properties.

A matching implementation in Java is shown below for the class Pet
The implementation of the class Cat
is very simple as we only need to extend the existing Pet
class. The only difference is that a Cat
can make a specific noise, namely Miauw
.
A small demo app could be:
Which would output:
Adding a Dog
class is not all that difficult. Just extend
the Pet
class and a method to allow the Dog
to bark.
This would result in the following implementation in Java:
Extending the main application to test the Dog
class could result in:
Which would output:
Note that while Dog
is-a Pet
and Cat
is-a Pet
, we cannot ask the Cat
to bark()
or the Dog
to miauw()
.
Accessibility
Attributes and methods are declared with an access specifier such as private
, protected
or public
. These allow the developer to determine who can access the class, attributes or methods.
When inheriting from a base class it is important to understand that you cannot access the private attributes or methods of the base class inside your subclass.
Let's take a look at our Pet example.
In the Pet
class we declared name
and age
to be private
(as it should), but that also means that the Cat
and Dog
classes cannot access these attributes directly, even while they did inherit them.
This means that the following implementation of a Dog
barking would not be feasible as name
would not be accessible from the subclass Dog
:
To fix this, two options are available:
the attributes
name
andage
could be madeprotected
instead ofprivate
, meaning that they cannot be accessed from outside of thePet
class, except from classes inheriting fromPet
.adding getters and/or setters for the attributes of the base this.
The first approach would replace the private
access modifier with the protected
modifier as shown below:
In most cases the second approach takes preference. Because otherwise the subclass could misuse the attributes. Guarding attributes using getters and setters is most often the preferred approach.
Resulting in the UML diagram:

Let's take another example: consider a class SpaceObject
with a subclass Planet
. Than we also create a class Space
which is composed of several Planet
s. As shown below, protected
attributes and methods are noted using the #
symbol in UML.
In the example the size
of a SpaceObject can only be accessed by SpaceObject
itself, not even by the subclass Planet
. However origin
is accessible by both SpaceObject
and all of its subclasses (such as Planet
). However not accessible from outside. MAX_SIZE
is a final
and static
class variable which is made public
and so accessible by all. However as it is final
it can only be read and not written.
Let's make an overview
Attribute of SpaceObject
Accessible by Planet?
Accessible by Space?
size
NO
NO
origin
YES
NO
MAX_SIZE
YES
YES
The same rules apply for access modifiers of methods.
With all this in mind we could change the implementation of bark()
with:
and for our class Cat
:
Which would result in the output:
Constructors and inheritance
When creating objects, Java will not only call the constructor of the type you are creating but it will implicitly call a constructor of each base class. Let's take a look at the inheritance hierarchy below.
When for example creating an object of type ServerProcessor
, the constructor of ServerProcessor
will implicitly call the constructor of Processor
which will call the constructor of Hardware
which will call the constructor of Product
. These calls are provided by default by Java and are done before anything else. That means that the rest of you constructor code will be executed after the constructor call to the base class.
This basically means that the Product
will be constructed first, next the Hardware
, after which the Processor
and last the ServerProcessor
. This is a bit logical as you can only initialize the specific data of Hardware
after the data of Product
has been initialized.
There is however a catch to this whole construction system.
Remember that if you do not define a constructor in Java, it will provide you with a default constructor for a class. However once you create a constructor yourself Java will not provide this default constructor anymore. That means if you create a single constructor that takes arguments, your class will not have a default constructor anymore.
Let us take a look at a more advances version of the previous Pet
class where we add an initialization constructor that takes the name
and age
of the Pet
as arguments:
The implementation above defines a single constructor taking a name
and an age
. This means that we can only construct objects using that constructor an NOT as follows:
Now take a subclass Bunny
of Pet
as defined below. As can be seen a single constructor is provided.
The implementation of Bunny
will actually not work because Java will add an implicit call to the default constructor of Pet
as the first line of code in the Bunny
class. In other words, currently no default constructor exists for the base class Pet
, resulting in an application that will fail.
This can be fixed using two approaches:
add a default constructor to the base class. This is however not always possible or even advisable as you may not have access to the implementation of the base class or it might not make sense to add a default constructor.
add an explicit call to the correct constructor of the base class. This can be achieved by using the keyword
super
which can be called as a methodsuper()
to indicate that a base class constructor needs to be called first.
The second approach mostly takes the preference. Important to note here is that this call to the base class constructor has to happen before anything else in the constructor. This means that super()
will be the first line of code inside your constructor in this case.
Let us apply this knowledge to the Bunny
class.
To summarize:
Java provides a default constructor if you provide no constructor(s). This is the one that takes no arguments.
With inheritance each constructor is called from bottom to top but actually executed from top to bottom.
If no default constructor exists for the base class you will need to add one or call another constructor explicitly as the first line of code in the current class constructor using
super()
and provide the required arguments.
Last updated