Java Reskilling 02: Advanced Object-Oriented Programming Concepts: Inheritance
Avant cette quête, tu dois avoir terminé la quête suivante :
Concepts avancés de la programmation orientée objet : l'inheritance (héritage)
Dans les quêtes sur la programmation orientée objet (POO), tu vas apprendre les concepts de l'OOP (POO), les bases de Java, le Java SDK et l'utilisation des bibliothèques standard de Java avec des exemples tirés du domaine de notre application exemple Tariff-Manager.
Dans cette quête sur l'inheritance, on va s'occuper des différents types de clients de notre application web Tariff-Manager.

👀 Introduction
Par définition, la programmation orientée objet te permet de penser ton code de façon abstraite, avec les états et les comportements concernés, au lieu de le voir simplement comme une suite d'instructions.
La classe Customer de l'exemple précédent avait des états (des attributs) appelés name, birthdate et active. Côté comportements, elle avait la méthode d'instance buy ainsi que la méthode statique sayWhatYouAre.
Et si on voulait créer une nouvelle classe appelée Employee ? Si on pense de façon abstraite, Customer et Employee ont des choses en commun, comme un nom et une date de naissance. On pourrait dire qu'ils sont tous les deux un User dans notre système. Un Customer est un User qui achète des produits dans notre entreprise, un Employee est un User qui travaille dans notre entreprise. Les deux partagent un nom et une date de naissance, mais ont aussi des attributs spécifiques différents comme lastPurchase et entryDate.

Le but final est de créer une classe Employee, mais comme beaucoup de ses états et comportements sont partagés avec Customer, comment éviter de dupliquer tout le code ?
C'est là que l'inheritance entre en jeu !
🤓 À la fin de cette quête, tu vas :
✅Connaître des composants avancés du langage Java : les classes abstraites (abstract classes), implements et extends ✅Comprendre des concepts avancés de la programmation orientée objet : les classes abstraites, l'inheritance, les relations is-a (est-un) ✅Être capable d'appliquer Java à des solutions métier avec des graphes de classes et d'objets en utilisant l'inheritance
Contenu de la quête
- Les classes et méthodes abstraites
- L'inheritance
👉 Les classes et méthodes abstraites
Les classes abstraites
Tu as la classe Customer, et tu veux la rendre plus générale en créant une classe abstraite User. Un customer aura la plupart des mêmes attributs qu'un user, comme un nom et une date de naissance.
Alors allons-y, créons la classe User :
public abstract class User {
// Attributes
private String name;
private LocalDate birthdate;
// Constructor
public User(String name) {
this.name = name;
}
// getters and setters
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthdate() {
return this.birthdate;
}
public void setBirthdate(LocalDate birthdate) {
this.birthdate = birthdate;
}
}
Les plus observateurs d'entre vous ont peut-être remarqué le mot-clé abstract dans la déclaration de la classe.
En effet, un user n'est pas une entité précise : on peut imaginer nommer un customer sur un marché, mais un user est une entité plutôt abstraite. De la même façon, tu peux imaginer conduire une voiture d'une marque et d'un modèle précis, mais essayer d'imaginer conduire un « véhicule » en général, c'est plus difficile.
Une classe abstraite ne peut pas avoir d'instance.
Il t'est donc impossible de créer un objet User.
Je te vois déjà là avec ton air perdu en disant : « Quoi, pas d'instance ?! ».
Reprenons notre exemple de la quête sur la programmation orientée objet : souviens-toi qu'en créant un nouvel objet avec la classe Customer, on crée une instance de cette classe.
Customer joe = new Customer("Joe", LocalDate.parse("06/09/1934"));
En initialisant ces attributs, un espace dans la mémoire de l'ordinateur est créé pour y sauvegarder notre customer. De plus, la référence joe pointe vers cet espace dans la mémoire.

Ce processus d'instanciation sera impossible avec une classe abstraite.
Mais à quoi sert une classe abstraite si elle ne peut pas être instanciée, me demandes-tu ?
Elle est là pour créer des comportements qui seront hérités par ses classes enfants.
Les méthodes abstraites
Une méthode abstraite est une méthode qui n'a pas d'implémentation.
Tu as de nouveau cet air perdu... Ne pas avoir d'implémentation, ça veut dire que la méthode n'a pas de corps, seulement une signature.
Prends par exemple les oiseaux. Ils vont avoir un chant différent selon leur espèce : canard, aigle, corbeau, etc...
Quelle que soit l'espèce de l'oiseau, il aura un chant, mais tu ne peux pas prédéfinir lequel ce sera, à moins de savoir exactement de quelle espèce d'oiseau il s'agit.
Ici, dans notre classe User, on va définir une méthode abstraite doSomething :
public abstract void doSomething();
Comme tu peux le voir, il n'y a pas d'accolades pour définir le corps de cette méthode, parce que ce sont les classes qui vont hériter de Customer qui devront définir le comportement de la méthode.
Donc, User précise que tous les users spécifiques auront la capacité de doSomething(). Pour le Customer ça pourrait être afficher I am buying a CrazyGreenDress et pour un Employee ça pourrait être I am working on this new agile development project.
On a beaucoup parlé des classes héritées, sans expliquer ce qu'elles sont. Regardons-les de plus près dans la section suivante !
👉L'inheritance
Parent et enfants
Une fois qu'on a notre classe abstraite User, on va pouvoir l'utiliser comme classe parent et faire hériter notre nouvelle classe Customer.
L'inheritance, du moins en programmation orientée objet, signifie qu'une classe enfant aura le droit d'accéder aux états et comportements de sa classe parent.
Par exemple, tu peux créer une classe abstraite Vehicle qui aura la marque et le kilométrage comme attributs. Tu peux ensuite créer des classes enfants Boat et Car qui vont en hériter.
Revenons à notre classe User. Imagine que tu crées une classe Customer qui hérite de la classe Bird, tu peux l'écrire en code comme ceci :
public class Customer extends User {
}
Tu remarques peut-être le mot-clé extends. C'est le mot-clé qui gère l'inheritance.
Une classe ne peut hériter que d'une seule autre classe.
En l'état, cette classe ne pourrait jamais passer la compilation pour deux raisons :
- Elle n'a pas de constructeur
- Elle n'implémente pas la méthode
doSomething
Constructeur et super
Comme la classe Customer n'est pas abstraite, elle peut être instanciée. Pour ça, tu vas devoir préciser un constructeur. Cependant, elle hérite de User, qui a lui-même un constructeur, qui initialise name avec la valeur de l'argument.
Ce serait intéressant d'essayer d'appeler ce constructeur plutôt que d'en créer un de zéro. Voici comment tu vas faire :
public Customer(String name) {
super(name);
}
Ce constructeur n'a pas le même contenu qu'un constructeur traditionnel : tu as peut-être remarqué l'utilisation du mot-clé super !
super sert à appeler une méthode telle qu'elle est définie dans le parent.
Dans ce cas, il appelle le contenu du constructeur du parent, et instancie donc un objet Customer en associant la valeur de l'argument à son attribut name.
L'avantage de ça, c'est d'éviter une duplication inutile de code. Mais si Customer était totalement identique à la classe parent, il n'y aurait aucun intérêt à le créer.
En gardant ça en tête, on va maintenant voir comment étendre (extend) les états et comportements d'une classe héritée.
L'implémentation des méthodes abstraites
La méthode abstraite doSomething dans User attend d'être implémentée dans chaque classe héritée. Comme on l'a dit avant, chaque espèce d'oiseau aura un chant unique.
Pour répondre à cette attente, tu vas devoir inclure la méthode doSomething et son corps comme suit :
@Override
public String doSomething() {
return "I am buying this CrazyGreenDress";
}
Il n'est pas nécessaire d'utiliser @Override ici. C'est seulement là pour faire savoir au développeur que la méthode est héritée et modifiée.
Extends

La notion d'inheritance repose sur l'idée d'étendre la classe parent, d'où l'utilisation du mot-clé extends. Ça revient à pouvoir modifier ou ajouter des choses dans une classe enfant.
Prenons le cas de notre customer. Tu veux lui ajouter un attribut lastPurchase qui est réglé sur la date du dernier achat du customer, tout en conservant les attributs name et birthdate de User.
Tu vas pouvoir modifier la classe comme suit :
public class Customer extends User {
private LocalDate lastPurchase;
public Customer(String name, LocalDate birthdate) {
super(name, birthdate);
}
@Override
public String doSomething() {
return "I am buying!";
}
public LocalDate getLastPurchase() {
return lastPurchase;
}
public void setLastPurchase(LocalDate lastPurchase) {
this.lastPurchase = lastPurchase;
}
}
Comme tu peux le voir ici, seul le nouvel attribut a été ajouté, et le constructeur a été modifié en conséquence pour initialiser l'attribut active après avoir appelé super, qui exécute le constructeur de User.
Tu peux modifier le constructeur de cette classe, par exemple, en initialisant l'attribut lastPurchase à null :
public Customer(String name) {
super(name);
this.lastPurchase = null;
}
Note que si l'attribut n'est pas défini, les types ont des valeurs par défaut. Pour les objets en Java, cette valeur par défaut est toujours null, pour un boolean c'est false, pour un int c'est 0.
🔬Exercice
Crée la classe Employee qui étend (extends) User :
- pour un attribut
entrydatequi est unLocalDateet donne l'information sur le début de l'emploi de cetEmployee. - pour une méthode
displayEmploymentInformation()qui retourne une String I am NAME and I am working here since ENTRYDATE où NAME est l'attributnameet ENTRYDATE est l'attributentrydate.
See solution|||java|||Reveal class Employee with the method checkIfEmployed()|||0|||Hide solution
public class Employee extends User {
private LocalDate entrydate;
public Employee(String name, LocalDate birthdate) {
super(name, birthdate);
// if no entrydate is passed, it is assumed to be today
this.entrydate = LocalDate.now();
}
public Employee(String name, LocalDate birthdate, LocalDate entrydate) {
super(name, birthdate);
this.entrydate = entrydate;
}
public String displayEmploymentInformation() {
return "I am " + getName() + " and I am working here since " + entrydate;
}
}
☝️Résumé
On a couvert beaucoup de concepts, tu sais maintenant comment :
- Créer des classes abstraites
- Créer des classes qui héritent de classes abstraites
- Créer des instances de classes qui héritent d'une classe abstraite
- Créer des références vers ces instances
- Appeler des méthodes étendues depuis des classes héritées
🔬Plongée dans Tariff-Manager
Pour explorer davantage ces sujets, clone l'application exemple Tariff-Manager, bascule sur la branche block-2-complete, ouvre les classes du package dev.wcs.nad.tariffmanager.customer.model et regarde comment les classes sont organisées.
💪Challenge
Ce sera un challenge sous forme de Quizz.
Références
- Photo de Daniel K Cheung sur Unsplash
Prior to this quest, you must have completed the following quest:
Advanced Object-Oriented Programming Concepts: Inheritance
In the quests about Object-Oriented Programming (OOP) you will learn concepts of OOP, Java basics, the Java SDK, and using the Java Standard Libraries with examples from the domain of our sample application Tariff-Manager.
In this quest about inheritance, we are dealing with different types of customers of our Tariff-Manager Web application.

👀 Introduction
By definition, object-oriented programming allows you to think abstractly about your code and the relevant states and behaviours, as opposed to viewing it simply as a sequence of instructions.
The Customer class of the last example had states (attributes) called name, birthdate and active. As for behaviors, it had the instance method buy as well as the static method sayWhatYouAre.
What if we want to create a new class called Employee? If we think abstractly, Customer and Employee have some things is common, like a name and a birthdate. We could say they are both User in our system. A Customer is a User who buys products at our company, an Employee is a User who works in our company. Both share names and birthdates, but also have different specific attributes like lastPurchase and entryDate.

The ultimate goal is to create an Employee class, but since many of its states and behaviors are shared with Customer, how can we avoid duplicating all the code?
This is where inheritance comes in!
🤓 By the end of this quest, you will:
✅Know advanced components of the Java language: abstract classes, implements, and extends ✅Understand advanced concepts of object-oriented programming: abstract classes, inheritance, is-a relationships ✅Be able to apply Java for domain solutions with class and object graphs using inheritance
Content of the Quest
- Abstract classes and methods
- Inheritance
👉 Abstract classes and methods
Abstract classes
You have the Customer class, and you want to make it more general by creating an abtract User class. A customer will have most of the same attributes as a user, like having a name and a birthdate.
So let's go ahead and create the class User:
public abstract class User {
// Attributes
private String name;
private LocalDate birthdate;
// Constructor
public User(String name) {
this.name = name;
}
// getters and setters
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public LocalDate getBirthdate() {
return this.birthdate;
}
public void setBirthdate(LocalDate birthdate) {
this.birthdate = birthdate;
}
}
The eagle-eyed among you, may have noticed the keyword abstract in the class declaration.
Indeed, a user is not a specific entity: we can imagine naming a customer on a market, but a user quite an abstract entity. Similarly, you will be able to imagine driving a car of a specific make and model, but trying to imagine driving a "vehicle" in general, is more difficult.
An abstract class cannot have an instance.
It is therefore impossible for you to create a User object.
I already see you standing there with a confused look saying: "What, no instance?!".
Let's take another look at our example from the quest on object-oriented programming: remember that by creating a new object with the Customer class we are creating an instance of this class.
Customer joe = new Customer("Joe", LocalDate.parse("06/09/1934"));
By initializing these attributes, a space in the computer’s memory is created to save our customer to. In addition, the reference joe points to this space in the memory.

This instantiation process will be impossible with an abstract class.
What is the point of having an abstract class if it can’t be instantiated, I hear you ask?
It’s there to create behaviours that will be inherited by its child classes.
Abstract methods
An abstract method is a method that has no implementation.
You’ve got that confused face again... Not having an implementation means that the method does not have a body, only a signature.
Take for example birds. They will have a different song according to their species: duck, eagle, raven, etc....
Whatever the species of bird, it will have a song, but you can't pre-define which one it will be, unless you know exactly what species of bird it is.
Here, in our User class, we will define an abstract method doSomething:
public abstract void doSomething();
As you can see, there are no curly braces to define the body of this method, because it is the classes that will inherit from Customer that will have to define the behavior of the method.
So, the User specifies that all specific users will have the ability of doSomething(). For the Customer that might be printing I am buying a CrazyGreenDress and for an Employee that could be I am working on this new agile development project.
We've been talking about inherited classes a lot, without explaining what they are. Let's look take a closer look at them in this next section!
👉Inheritance
Parent and Children
Once we have our abstract User class, we will be able to use it as a parent class and have our new Customer class inherit it.
Inheritance, at least in object-oriented programming, means that a child class will have the right to access the states and behaviours of its parent class.
For example, you can create an abstract class Vehicle that will have brand and mileage as attributes. You can then create Boat and Car child classes that will inherit from it.
Let's come back to our User class. Imagine that you create an Customer class that inherits from the Bird class, you can write that in code like this:
public class Customer extends User {
}
You might notice the keyword extends. This is the keyword that manages the inheritance.
A class can only inherit from one other class.
As such, this class would never be able to pass the compilation for two reasons:
- It has no constructor
- It does not implement the
doSomethingmethod
Constructor and super
Because the Customer class is not abstract, it can be instantiated. To do this, you’ll need to specify a constructor. However, it inherits User, which itself has a constructor, which initializes name with the argument value.
It would be interesting to try and call this constructor as opposed to creating one from scratch. Here's how you're going to do it:
public Customer(String name) {
super(name);
}
This constructor does not have the same content as a traditional constructor: you may have noticed the use of the keyword super!
super is used to call a method as defined in the parent.
In this case, it is calling the content of the parent's constructor, and therefore instantiating an object Customer and associating the value of the argument with its attribute name.
The advantage of this is to avoid unnecessary duplication of code. But if Customer were totally identical to the parent class, there would be no point in creating it.
Bearing that in mind, we will now cover how to extend the states and behaviours of an inherited class.
Implementation of abstract methods
The abstract method doSomething in User expects to be implemented within each inherited class. As we said before, every bird species will have a unique song.
To meet this expectation, you will need to include the doSomething method and its body as follows:
@Override
public String doSomething() {
return "I am buying this CrazyGreenDress";
}
It’s not necessary to use @Override here. It is only there to let the developer know that the method is inherited and modified.
Extends

The notion of inheritance is based on the idea of extending the parent class, hence the use of the keyword extends. This boils down to being able to modify or add things in a child class.
Let's take the case of our customer. You want to add a lastPurchase attribute to it that is set to the date of the last purchase by the customer, while also retaining the name and birthdate attributes of User.
You will be able to modify the class as follows:
public class Customer extends User {
private LocalDate lastPurchase;
public Customer(String name, LocalDate birthdate) {
super(name, birthdate);
}
@Override
public String doSomething() {
return "I am buying!";
}
public LocalDate getLastPurchase() {
return lastPurchase;
}
public void setLastPurchase(LocalDate lastPurchase) {
this.lastPurchase = lastPurchase;
}
}
As you can see here, only the new attribute has been added, and the constructor has been modified accordingly to initialise the active attribute after calling super, which executes the User constructor.
You can modify the constructor of this class, for example, by initialising the attribute lastPurchase to null:
public Customer(String name) {
super(name);
this.lastPurchase = null;
}
Note that if the attribute is not set, types have default values. For objects in Java, this default value is always null, for boolean it is false, for int it is 0.
🔬Exercise
Create the class Employee which extends User:
- for an attribute
entrydatewhich isLocalDateand gives information about the start of the employment of thisEmployee. - for a method
displayEmploymentInformation()which returns a String I am NAME and I am working here since ENTRYDATE where NAME is attributenameand ENTRYDATE is attributeentrydate.
See solution|||java|||Reveal class Employee with the method checkIfEmployed()|||0|||Hide solution
public class Employee extends User {
private LocalDate entrydate;
public Employee(String name, LocalDate birthdate) {
super(name, birthdate);
// if no entrydate is passed, it is assumed to be today
this.entrydate = LocalDate.now();
}
public Employee(String name, LocalDate birthdate, LocalDate entrydate) {
super(name, birthdate);
this.entrydate = entrydate;
}
public String displayEmploymentInformation() {
return "I am " + getName() + " and I am working here since " + entrydate;
}
}
☝️Summary
We’ve covered a lot of concepts, you know now how to:
- Create abstract classes
- Create classes which inherit from abstract classes
- Create instances of classes that are inheriting from an abstract class
- Create references to these instances
- Call extended methods from inherited classes
🔬Tariff-Manager Deep Dive
To examine these topics more, clone the sample application Tariff-Manager, switch to the branch block-2-complete, open the classes in package dev.wcs.nad.tariffmanager.customer.model and take a look at how the classes are organized.
💪Challenge
This will be a Quizz challenge.
References
- Photo by Daniel K Cheung on Unsplash