certification java

OCPJP 6 – Jour 4 – Classes abstraites et interfaces

Une classe abstraite et une interface ne peuvent être instanciées. Le mécanisme des classes abstraites permet de définir des comportements (méthodes) dont l’implémentation (le code dans la méthode) se fait dans les classes filles. Ainsi, on a l’assurance que les classes filles respecteront le contrat défini par la classe mère abstraite. Une interface est une classe 100% abstraite où toutes les méthodes sont abstraites et tous les attributs sont des constantes.

4.1 – Les classes abstraites

4.1.1 – L’intérêt des classes abstraites avec un exemple concret

Abstract_Interfaces_1

Cette structure de classes est relativement satisfaisante. Celle-ci a été conçue pour réduire au maximum la duplication de code et les méthodes, dont l’implémentation est spécifique à un type d’aéronef, ont été redéfinies dans les sous-classes.

Il est ainsi possible d’instancier différents aéronefs militaires :

Mais dans cette configuration, il est aussi possible de faire :

Cela a un sens de créer un objet F16 ou un objet Chinook mais qu’est-ce exactement qu’un objet MilitaryAircraft ? Quelle est sa forme ? Comment vole-t-il ? Avec quelle énergie ? Certaines classes ne doivent tout simplement pas être instanciées ! Il existe un moyen simple d’empêcher d’instancier une classe, autrement dit d’employer le mot-clé new.

Abstract_Interfaces_2

4.1.2 – Définition

Une classe abstraite est une classe qui ne permet pas d’instancier des objets. Elle ne peut servir que de classe de base pour une dérivation.

Une classe abstraite est précédée du mot-clé abstract :

Dire qu’une classe est abstraite signifie que personne ne pourra jamais en créer une nouvelle instance. Mais elle reste toujours utilisable pour définir le type d’une référence pour les besoins du polymorphisme.

Le compilateur génèrera l’erreur suivante :

Une classe abstraite n’a virtuellement aucune utilité, aucune valeur, aucune raison d’être, à moins qu’elle ne soit étendue.

4.1.3 – Méthodes abstraites

Abstract_Interfaces_3


Outre les classes, vous pouvez également définir des méthodes abstraites. Tout comme une classe abstraite doit être étendue, une méthode abstraite doit être redéfinie. Vous pouvez décider que tout ou partie des comportements d’une classe abstraite n’ont pas de sens s’ils ne sont pas implémentés par une sous-classe spécifique. En effet, on peut placer dans une classe abstraite toutes les fonctionnalités dont on souhaite disposer pour toutes ses descendantes :

  • soit sous forme d’une implémentation de méthodes non abstraites lorsque celles-ci sont communes à toutes les sous-classes
  • soit sous forme de méthodes abstraites dont on est sûr qu’elles existeront dans toute classe dérivée instanciable.

Une méthode abstraite est une méthode précédée du modificateur abstract sans corps.

Le prototype d’une méthode abstraite est suivi d’un point-virgule et ne possède pas d’accolades.
La présence d’une méthode abstraite au sein d’une classe, vous oblige à déclarer la classe comme étant elle-même abstraite sous peine d’obtenir une erreur du compilateur :

Le compilateur génèrera l’erreur suivante :

Cependant l’inverse, à savoir une classe abstraite ne possédant aucune méthode abstraite, est tout à fait possible et vous pouvez très bien mélanger méthodes concrètes et abstraites :

Définir une méthode abstraite, au sein d’une classe, oblige à implémenter le corps de la méthode en question dans chacune de ses classes filles, à moins que ces classes filles ne soient elles-mêmes déclarées abstraites.

La première classe fille concrète (non abstraite) d’une classe abstraite doit implémenter toutes les méthodes abstraites de sa classe mère.

Exemple :

Abstract_Interfaces_4

La méthode startEngine() est abstraite dans la classe mère MilitaryAircraft et n’est pas implémentée (ni même redéclarée) dans la classe abstraite Helicopter. Cela signifie que cette méthode doit être obligatoirement implémentée dans la classe Tiger qui constitue, dans notre exemple, la première classe concrète.

Informations complémentaires


Examinons le code suivant :

La classe UAV ne compilera pas car, malgré les apparences, la méthode abstraite gainAltitude() de la classe mère MilitaryAircraft n’est pas redéfinie et implémentée dans la classe fille UAV. On parle ici de surcharge de méthode (signature identique mais arguments différents).

Enfin le modificateur abstract ne peut être associé aux modificateurs final, private et static.
En effet, ces associations sont contradictoires. Par définition, une méthode final ou static ne peut être redéfinie dans une sous-classe et une méthode private a une visibilité limitée à sa propre classe.

4.2 – Les interfaces

Nous venons de voir comment une classe abstraite permettait de définir dans une classe de base des fonctionnalités communes à toutes ses descendantes, tout en leur imposant de redéfinir certaines méthodes. Nous allons voir que la notion d’interface est plus riche qu’une classe abstraite.

En effet :

  • une classe peut implémenter plusieurs interfaces (alors q’une classe ne peut dériver que d’une seule classe abstraite, l’héritage multiple n’existant pas en Java)
  • la notion d’interface va se superposer à celle de dérivation, et non s’y substituer. Une classe peut, à la fois, étendre une classe et implémenter une ou plusieurs interfaces.

Des classes venant d’arbres d’héritage complètement différents peuvent implémenter une même interface. Vous pouvez ainsi traiter un objet en fonction du rôle qu’il joue, plutôt qu’en fonction du type de la classe à partir de laquelle il a été instancié.

Abstract_Interfaces_5

4.2.1 – Définition

Une interface définit un comportement (d’une classe) qui doit être implémenté par une classe, sans implémenter ce comportement. Une interface est une classe 100% abstraite.

Une interface se déclare de la façon suivante :

Le modificateur abstract appliqué à l’interface est optionnel car implicite. Une interface est toujours abstraite. Par contre le modificateur public est requis si on souhaite que l’interface ait une visibilité publique contrairement à, sans modificateur, une visibilité par défaut.

De même ce code est strictement équivalent :

4.2.2 – Les méthodes d’une interface

Contrairement à une classe abstraite qui peut à la fois posséder des méthodes abstraites et non abstraites, une interface ne peut avoir que des méthodes abstraites. Comme toutes les méthodes sont abstraites, toute classe qui implémente une interface doit redéfinir toutes les méthodes de cette interface. Sachant que les méthodes composant une interface sont 100% abstraites, les modificateurs public et abstract appliqués aux méthodes composant l’interface sont optionnels car implicites.

Les méthodes d’une interface sont toujours implicitement publiques et abstraites même si celles-ci ne possèdent pas le mot-clé public et abstract.

De même ce code est strictement équivalent :

4.2.3 – Implémentation(s) d’une interface

La classe implémentant une interface se déclare de la façon suivante :

Une interface, étant une classe 100% abstraite, la classe Chinook doit implémenter toutes les méthodes de l’interface Transport.

Une même classe peut implémenter plusieurs interfaces :

La classe Chinook doit implémenter toutes les méthodes de l’interface Transport, mais aussi toutes celles de l’interface War.

Une classe abstraite peut aussi implémenter une interface :

Abstract_Interfaces_6

Les méthodes landingArea() et weatherReport() de l’interface Heliport ne sont pas implémentées dans la classe abstraite Helicopter. Ce qui implique que ces deux méthodes doivent être implémentées dans chacune des sous-classes Chinook et Tiger.

Une interface peut étendre une autre interface :

Abstract_Interfaces_7

Dans l’exemple ci-dessus, les classes Chinook et Tiger doivent à la fois implémenter les méthodes de l’interface Heliport mais aussi celles de l’interface Airport car elles constituent les première classes concrètes de cet arbre.

Si l’héritage multiple entre classes n’est pas autorisé en Java, il l’est pour les interfaces. En effet une interface peut étendre plusieurs interfaces :

Abstract_Interfaces_8

Dans l’exemple ci-dessus, les classes Chinook et Tiger doivent à la fois implémenter les méthodes de l’interface Heliport mais aussi les méthodes des interfaces dont elle hérite : Airport et AircraftCarrier.

Quelques exemples invalides :

4.3 – Les constantes

Toutes les variables déclarées dans une interface doivent être public, static et final. En d’autres termes, toutes les variables déclarées dans une interface sont des constantes.

Chaque classe implémentant une interface a un accès direct aux constantes déclarées dans cette même interface.

Une constante est toujours publique, statique et abstraite. Ces modificateurs sont implicites, ainsi ces déclarations de constantes sont strictement identiques :

4.4 – Classes abstraites ou interfaces ?

  • Utilisez une classe abstraite quand vous voulez définir un patron pour un groupe de sous-classes, et que vous avez au moins un peu de code que toutes les sous-classes pourraient utiliser. Rendez la classe abstraite pour garantir que personne ne pourra créer d’objets de ce type.
  • Utilisez une interface pour définir un rôle que d’autres classes puissent jouer, indépendamment de leur place dans la structure d’héritage.


Auteur
AuteurEdouard WATTECAMPS

0 réponses

Répondre

Se joindre à la discussion ?
Vous êtes libre de contribuer !

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *