Java Reskilling 03: Advanced Object-Oriented Programming Concepts: Interfaces
Avant cette quête, tu dois avoir terminé les quêtes suivantes :
Concepts avancés de la programmation orientée objet : les interfaces
Dans les quêtes sur la programmation orientée objet (POO) tu vas apprendre les concepts de la 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 les interfaces, nous allons calculer des remises pour nos différents types de clients.

👀 Introduction
On a vu plus tôt qu'une classe ne peut hériter que d'une seule autre classe.
Imagine que tu as les classes DiscountCalculator et PriceCalculator, qui héritent toutes les deux de Calculator. Elles sont chargées de calculer des valeurs à partir d'attributs et de paramètres, mais elles n'ont pas de lien entre elles. Elles calculent des choses différentes !
Tu veux donc exprimer que DiscountCalculator et PriceCalculator sont toutes les deux capables de calculer des choses, mais tu ne veux pas qu'elles étendent une classe générique quelconque car elles n'ont rien de plus en commun que le fait de pouvoir calculate.
N'aie aucune crainte, les interfaces sont là pour t'aider !
🤓 À la fin de cette quête, tu sauras :
✅Connaître les composants avancés du langage Java : interface, méthodes par défaut (default methods), et implements ✅Comprendre les concepts avancés de la programmation orientée objet : méthodes abstraites et d'interface, héritage multiple ✅Être capable d'appliquer Java à des solutions métier avec un développement basé sur des contrats grâce aux interfaces et aux classes abstraites
Contenu de la quête
- Les interfaces en tant que contrats
- Les implémentations d'interfaces
- Les méthodes par défaut
👉Les interfaces en tant que contrats
Chaque fois que tu entends parler des interfaces Java, tu as toutes les chances d'entendre : une interface est un contrat.
C'est vrai, une interface (contrat que les classes doivent respecter) définit un ensemble de comportements, sans les décrire précisément. Un calculateur peut calculer, être réinitialisé et persisté, mais pour les calculateurs de remise et les calculateurs de prix tu adopterais des approches différentes.
Comme on l'a dit, le rôle d'une interface est de définir des comportements, sans les décrire : ce sont les classes qui implémentent les interfaces qui déterminent le corps des méthodes.
Par exemple, nous avons l'interface Calculator qui va définir certains des comportements de notre calculateur :
public interface Calculator {
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input);
/**
* reset the calculator
*/
void reset();
/**
* is the calculator ready for calculations?
*/
boolean isReady();
}
Tu remarqueras peut-être l'utilisation du mot-clé interface ici. Il est utilisé à la place de class, pour une raison simple : une interface n'est pas une classe !

👉Les implémentations d'interfaces
Une interface n'est pas une classe, donc chaque fois que tu veux utiliser une interface, on parle d'implémentation (implementation) plutôt que d'héritage.
Si tu veux implémenter les comportements de l'interface Calculator à l'intérieur de la classe DiscountCalculator, tu dois utiliser le mot-clé implements :
public class DiscountCalculator implements Calculator {
public DiscountCalculator(BigDecimal[] discounts) {
super();
this.discounts = discounts;
}
}
Tu remarqueras peut-être dans l'exemple ci-dessus qu'il est possible de combiner un héritage avec une implémentation d'interface.
Et même s'il n'est possible d'hériter que d'une seule classe, il est possible d'implémenter plusieurs interfaces.
Tout ce que tu as à faire, c'est de séparer les interfaces par des virgules. Par exemple, ici la classe Car implémente à la fois les interfaces Motor et FourWheels :
public class Car extends Vehicle implements Motor, FourWheels {
// ...
}
Pour revenir à l'exemple de Calculator et DiscountCalculator, l'implémentation seule ne suffira pas. Quand tu essaieras de compiler, tu obtiendras l'erreur suivante :
DiscountCalculator is not abstract and does not override abstract method calculate() in `Calculator`
Comme on l'a vu dans la première partie de la quête, une interface est un contrat qui exige de la classe qui l'implémente de décrire ses comportements.
Ainsi, il est obligatoire de définir le corps des méthodes implémentées depuis Calculator à l'intérieur de la classe DiscountCalculator, comme ceci :
public class DiscountCalculator implements Calculator {
public DiscountCalculator(String name, int age) {
super(name, age);
}
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input) {
// ... implementation of DiscountCalculator calculate
}
/**
* reset the calculator
*/
public void reset() {
// ... implementation of DiscountCalculator reset
}
/**
* is the calculator ready for calculations?
*/
public boolean isReady() {
// ... implementation of DiscountCalculator ready
}
}
Dans le même esprit, voici un exemple d'implémentation des méthodes de Calculator à l'intérieur de la classe PriceCalculator :
public class PriceCalculator implements Calculator {
public PriceCalculator(String name, int age) {
super(name, age);
}
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input) {
// ... implementation of PriceCalculator calculate
}
/**
* reset the calculator
*/
public void reset() {
// ... implementation of PriceCalculator reset
}
/**
* is the calculator ready for calculations?
*/
public boolean isReady() {
// ... implementation of PriceCalculator ready
}
}
👉Les méthodes par défaut
Comme on l'a vu plus tôt, c'est la responsabilité de la classe qui implémente l'interface de décrire son comportement : autrement dit, de remplir le corps de ses méthodes.
Cependant, depuis la mise à jour Java 8, il est désormais possible d'attribuer un comportement par défaut aux méthodes d'une interface, à l'aide du mot-clé default.
Voici un exemple utilisant la méthode status() à l'intérieur de la classe Calculator :
default void displayStatus() {
System.out.println("Calculator is up and running.");
}
Parce que la méthode a un comportement par défaut, tu n'es plus obligé de définir son comportement dans toutes les classes qui l'implémentent.
Cependant, il t'est toujours possible de redéfinir ton comportement grâce au polymorphisme.
Voici un exemple à l'intérieur de la classe ComplexCalculator :
@Override
public void displayStatus() {
if (externalSystemsReady()) {
System.out.println("Calculator is up and running.");
} else {
System.out.println("Something is not right, external systems are not available.");
}
}
🔬Exercice
Crée la classe SalaryCalculator qui implémente Calculator :
- Le
namedoit indiquer un nom d'affichage pour le calculateur, comme pour les autres calculateurs, sous forme deString. - L'
inflationRatedoit être un taux d'inflation annuel attendu sous forme deBigDecimal, par exempleBigDecimal.valueOf("1.5")pour un taux d'inflation attendu de 1,5 %. - La
valuepassée lors de l'appel decalculatedoit être le salaire et la valeur retournée est le salaire ajusté par le taux d'inflation.
Note : la méthode calculate doit retourner
value.multiply(inflationRate.multiply(100))
Reveal the SalaryCalculator|||java||||||1|||
public class SalaryCalculator implements Calculator {
private final BigDecimal inflationRate;
public SalaryCalculator(BigDecimal inflationRate) {
this.inflationRate = inflationRate;
}
@Override
public BigDecimal calculate(BigDecimal value) {
return value.multiply(BigDecimal.valueOf(100).multiply(inflationRate));
}
@Override
public void reset() {
// if reset() is not supported for this specific implementation,
// an UnsupportedOperationException should the thrown
throw new UnsupportedOperationException("SalaryCalculator cannot be reset.");
}
@Override
public boolean isReady() {
return true;
}
}
☝️Résumé
Dans cette quête, tu as appris à...
- Créer une classe abstraite
- Créer une interface
- Créer des classes héritant de classes abstraites
- Créer des implémentations d'interfaces
- Appeler les méthodes implémentées
- Appeler une méthode avec un comportement par défaut
🔬Plongée dans Tariff-Manager
Pour approfondir 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 de type Quizz.
Références
- Photo de Shubham Dhage sur Unsplash
Prior to this quest, you must have completed the following quests:
Advanced Object-Oriented Programming Concepts: Interfaces
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 interfaces, we are calculating discounts for our different types of customers.

👀 Introduction
We saw earlier that a class can only inherit from one other class.
Imagine that you have the classes DiscountCalculator and PriceCalculator, which both inherit from Calculator. They are responsible to calculate values based on attributes and parameters, but they are not related to each other. They are calculating different things!
So you want to express that DiscountCalculator and PriceCalculator are both able to calculate things, but you do not want them to extend some generic class as they have nothing more in common than being able to calculate.
Have no fear, interfaces are here to help you!
🤓 By the end of this quest, you will:
✅Know advanced components of the Java language: interface, default methods, and implements ✅Understand advanced concepts of object-oriented programming: abstract and interface methods, multiple inheritance ✅Be able to apply Java for domain solutions with contract-based development using interfaces and abstract classes
Content of the Quest
- Interfaces as Contracts
- Implementations of Interfaces
- Default Methods
👉Interfaces as Contracts
Whenever you hear about Java interfaces, you are likely to hear: an interface is a contract.
It’s true, an interface defines a set of behaviours, without specifically describing them. A calculator can calculate, can be resetted and persisted, but for discount calculators and price calculators you would take a different approaches.
Like we said, the role of an interface is to define behaviours, without describing them: these are the classes that implement the interfaces and determine the body of the methods.
For example, we have the Calculator interface which will define some of our calculator's behaviours:
public interface Calculator {
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input);
/**
* reset the calculator
*/
void reset();
/**
* is the calculator ready for calculations?
*/
boolean isReady();
}
You might notice the use of the keyword interface here. It’s being used in place of class, for the simple reason that: an interface is not a class!

👉Implementations of Interfaces
An interface is not a class so whenever you want to use an interface, we talk about implementation instead of inheritance.
If you want to implement the behaviours of the Calculator interface inside the DiscountCalculator class, you must use the keyword implements:
public class DiscountCalculator implements Calculator {
public DiscountCalculator(BigDecimal[] discounts) {
super();
this.discounts = discounts;
}
}
You might notice in the above example that it is possible to combine an inheritance with an interface implementation.
And although it is only possible to inherit one class, it is possible to implement several interfaces.
All you have to do is separate the interfaces with commas. For example, here the Car class implements both the Motor and FourWheels interfaces:
public class Car extends Vehicle implements Motor, FourWheels {
// ...
}
Returning to the example of Calculator and DiscountCalculator, the implementation alone will not be enough. When you try to compile, you will get the following error:
DiscountCalculator is not abstract and does not override abstract method calculate() in `Calculator`
As we saw in the first part of the quest, an interface is a contract that requires the implementing class to describe its behaviors.
Thus, it is mandatory to define the body of methods implemented from Calculator within the DiscountCalculator class, like so:
public class DiscountCalculator implements Calculator {
public DiscountCalculator(String name, int age) {
super(name, age);
}
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input) {
// ... implementation of DiscountCalculator calculate
}
/**
* reset the calculator
*/
public void reset() {
// ... implementation of DiscountCalculator reset
}
/**
* is the calculator ready for calculations?
*/
public boolean isReady() {
// ... implementation of DiscountCalculator ready
}
}
In the same vein, here is an example of implementing the methods from Calculator inside the PriceCalculator class:
public class PriceCalculator implements Calculator {
public PriceCalculator(String name, int age) {
super(name, age);
}
/**
* calculate based on the input parameter
*/
BigDecimal calculate(BigDecimal input) {
// ... implementation of PriceCalculator calculate
}
/**
* reset the calculator
*/
public void reset() {
// ... implementation of PriceCalculator reset
}
/**
* is the calculator ready for calculations?
*/
public boolean isReady() {
// ... implementation of PriceCalculator ready
}
}
👉Default methods
As we saw earlier, it is the responsibility of the class that is implementing the interface to describe its behaviour: in other words, to fill-in the body of its methods.
However, since the Java 8 update, it is now possible to assign a default behavior to the methods of an interface, using the keyword default.
Here is an example using the status() method within the Calculator class:
default void displayStatus() {
System.out.println("Calculator is up and running.");
}
Because the method has a default behavior, you are no longer required to define its behaviour in all of the classes that implement it.
However, it is still possible for you to redefine your behaviour through polymorphism.
Here is an example inside the class ComplexCalculator :
@Override
public void displayStatus() {
if (externalSystemsReady()) {
System.out.println("Calculator is up and running.");
} else {
System.out.println("Something is not right, external systems are not available.");
}
}
🔬Exercise
Create the class SalaryCalculator which implements Calculator:
- The
nameshould indicate a display name for the calculator, similar to the other calculators, asString. - The
inflationRateshould be an expected yearly inflation rate as aBigDecimal, eg.BigDecimal.valueOf("1.5")for an expected 1.5% inflation rate. - The
valuepassed oncalculateinvocation should be the salary and the returned value is the salary adjusted by the inflation rate.
Note: the calculate method should return
value.multiply(inflationRate.multiply(100))
Reveal the SalaryCalculator|||java||||||1|||
public class SalaryCalculator implements Calculator {
private final BigDecimal inflationRate;
public SalaryCalculator(BigDecimal inflationRate) {
this.inflationRate = inflationRate;
}
@Override
public BigDecimal calculate(BigDecimal value) {
return value.multiply(BigDecimal.valueOf(100).multiply(inflationRate));
}
@Override
public void reset() {
// if reset() is not supported for this specific implementation,
// an UnsupportedOperationException should the thrown
throw new UnsupportedOperationException("SalaryCalculator cannot be reset.");
}
@Override
public boolean isReady() {
return true;
}
}
☝️Summary
In this quest, you learned how to...
- Create an abstract class
- Create an interface
- Create classes inheriting abstract classes
- Create implementations of interfaces
- Call the implemented methods
- Call a method with a default behaviour
🔬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 Shubham Dhage on Unsplash