Besoin de renseignement? Contactez nous....
Envoyer un Email +33 7 81 01 72 30
contact@odellya.com

Débuter avec Java – L’héritage

 

 

Objectifs : Comprendre la notion d’interface, d’une classe abstraite et celle de l’héritage.
Dans les autres tutos on a vu à quoi correspond une classe java concrète, cette fois, à travers ce tutoriel nous allons découvrir d’autres types de classes, voir la classe abstrait et l’interface ainsi que le mécanisme d’héritage de chaque type.

1. Une classe abstraite :


Une classe abstraite est une classe qui a la même structure qu’une classe normale mais qui peut en plus contenir des méthodes abstraites.

Rappel: une méthode abstraite est une méthode déclarée mais pas définie, elle n’a pas de corps.

Structure :
abstract class Nom_class
{
//on peut ajouter des attributs ;
abstractType_Retour Nom_Methode_Ab() ;
// on peut ajouter d’autres classes abstraites ou concrètes.
}

Comme une classe concrète, une classe abstraite peut avoir un constructeur mais on ne
pourra jamais construire une instance d’une classe abstraite, (ce constructeur sera utilisé par l’héritage).

Remarque : Il suffit d’englober une seule méthode abstraite pour que la classe soit considérée abstraite et doit être déclarée ainsi.

  • ▶ Les modificateurs d’accès:
    Sont les mêmes que ceux d’une classe concrète.
    ▶ Les modificateurs de non accès:
  • Sont les mêmes qu’une classe concrète sauf pour final, une classe ne peut pas être à la fois final et abstract car une classe abstraite est destinée à concrétiser ses méthodes par les classes filles.
  • ▶ Son utilité:
    Par exemple on va développer un système de gestion du personnel : directeur, chef de projet et techniciens.
    Le point commun entre les 3 c’est qu’ils sont tous des employés et ils passent tous par la fonction de pointage mais chacun d’eux passe par un entretien spécifique et possède un salaire diffèrent.
    Donc pour éviter la redondance de créer pour chaque classe une méthode pour marquer le pointage qui est la même pour tous, on déclare et on définit (écrire le code) et on déclare juste les deux méthodes entretien et salaire pour les implémenter grâce à l’héritage selon le poste de chaque employé.

 2. Les interfaces :


Une interface est une classe abstraite mais qui ne peut contenir que des méthodes abstraites (pas de méthodes concrètes).

Structure :
interface class Nom_Interface
{
//on peut ajouter des attributs ;
Type_Retour Nom_Methode_Ab() ;
}

Remarque : Toutes les méthodes des interfaces sont par défaut public, static et abstract donc l’ajout ou non de ces mots clés ne change rien.
Leurs attributs sont par défaut public et doivent être déclarés final.
Par contre la modification d’une de ces propriétés déclenche une erreur de compilation.

  • ▶ Les modificateurs d’accès:
  • Sont les mêmes que ceux d’une classe concrète.
  • ▶ Les modificateurs de non accès:
    Une interface ne peut être déclarée static ni final.
  • ▶ Son utilité:
    Par exemple on va traiter différentes maladies, la grippe, l’hépatite A et la varicelle.
    Les points communs entre les 3 c’est qu’elles sont toutes des maladies et quelles sont toutes des maladies contagieuses, mais chacune d’elles possède des symptômes et un remède spécifique.
    Donc pour éviter la redondance de déclarer un attribut Type_Maladie pour chaque classe de ces maladies qui seront toutes affectées par la même valeur et de créer pour chacune d’elles une méthode pour afficher les symptômes et une méthode pour afficher les remèdes on déclare ces méthodes dans une interface sans les définir puis l’héritage se chargera de leur implémentation.
  • 3. L’héritage :

L’héritage consiste à faire transmettre des propriétés (attributs et méthodes) d’une mère (classe concrète, classe abstraite ou interface) à des classes filles (classe concrète, classe abstraite ou interface).
La relation d’héritage exprime une relation de Is a (est un) entre la classe fille est la classe mère.

  • ▶ L’héritage entre les classes :
  • On va continuer avec l’exemple des employés mais avant je dois vous informer que le mot clé pour indiquer qu’il s’agit d’un héritage à partir d’une classe (concrète ou abstraite) est extends.

Remarque : L’héritage multiple est interdit par java ! Une même classe ne peut hériter qu’au plus d’une seule classe.

  • ▶ Héritage des méthodes :
  • On dit qu’un Technicien est un (is an) Employe.
    Après on crée une classe concrète « Technicien » qui va hériter de la classe Employe :
  • Ici le compilateur nous demande d’implémenter les méthodes abstraites héritées. Donc soit on les implémente en cliquant sur Add unimplemented methodes soit on ne le fait pas et dans ce cas il faut qu’on déclare la classe Technicien abstract :
  • Donc si on choisit de cliquer le résultat sera comme suit :
  • Donc dans le corps de chacune de ces deux méthodes on ajoute les instructions appropriées à l’entretien et au salaire du technicien.
    De même pour la méthode Pointage qui est déjà définie dans la classe Employe, elle peut soit être utilisée telle quelle par le Technicien ou bien modifier son code. Ces deux opérations se nomment la redéfinition.
  • Remarque : Une méthode abstraite ne peut pas être appelée seulement après sa redéfinition.
  • ▶ La redéfinition :
  • La redéfinition consiste à modifier essentiellement le contenu d’une méthode héritée, soit une méthode abstraite et l’implémenter soit une méthode concrète et modifier son traitement.
  • Et la redéfinition a certaines règles à respecter :
  • Interdit de remplacer un modificateur d’accès plus large par un autre d’une portée plus basse comme remplacer public par protected, le contraire est autorisé.
  • Interdit de changer l’entête : le nom, le type de retour, le nombre, le type et l’ordre des paramètres mais si la méthode héritée n’est pas abstraite donc un changement de l’entête sera considéré comme une nouvelle méthode et pas une redéfinition.

Supposons qu’on a redéfinie une méthode et qu’on souhaite quand même dans celle-ci faire appel à la méthode héritée, pour le faire java a réservé le mot clé super, on va continuer avec notre exemple :

Remarque : ni super ni this ne peuvent figurer dans une méthode statique.
Super ne peut pas être utilisé avec une méthode statique, donc la solution est de l’appeler précédée par le nom de la classe, comme suit :

Donc pour essayer avec le super, on va éliminer le static des méthodes Pointage dans les 2 classes pour que ça donne :

Donc comme le montre le résultat, super permet d’appeler la méthode héritée seulement si cette dernière n’est pas statique.

Remarque : de même pour les attributs si la classe mère et la classe filles possède chacune un attribut qui tous deux portent le nom, donc pour faire la différence on utilise this et super comme suit :
this.CIN = «  »;
super.CIN = «  » ;

a. Appel aux constructeurs de la classe mère :

Le constructeur de la sous-classe doit implicitement ou explicitement faire appel au constructeur de la super classe pour justifier la relation Is a. Donc si la classe mère admet un constructeur par défaut ou un constructeur vide, dans ce cas il sera appelé implicitement sinon si cette dernière admet que des constructeurs paramétrés, donc obligatoirement dans la classe fille au minimum un constructeur doit être construit dans lequel un appel explicite au constructeur paramétré de la classe mère, voyons ensemble un exemple :

On a ajouté un constructeur paramétré dans la classe Employe ce qui a déclenché une erreur dans la classe Technicien, pour corriger ça on doit y ajouter un constructeur paramétré ou pas qui fait appel au constructeur paramétré de la classe Employe :

Remarque :

  • super doit être la première instruction dans le constructeur.
  • super et this ne peuvent pas se trouver ensemble dans le même constructeur.
  • La redéfinition s’applique sur les méthodes abstraites comme sur les méthodes déjà définies.
  • La notation @Override indique au compilateur que la méthode en dessous est une redéfinition de la méthode héritée. Dans ce cas un changement de l’entête de la méthode initiale (2eme règle) en gardant le même nom, engendrera une erreur de la compilation et la méthode ne sera pas considérée comme une nouvelle méthode car elle était censée se faire redéfinir.
  • Dans le cas où on a changé l’entête de la méthode héritée qui est une méthode abstraite en gardant le même nom sans ajouter @Override pour but de créer une nouvelle méthode, de plus il faut quand même obligatoirement implémenter l’abstraite en respectant les 2 règles mentionnées plus haut.

On va faire un essai pour voir laquelle des méthodes sera appelée, la méthode de la classe mère ou bien celle redéfinie par la sous-classe :

On a changé la valeur de retour false par true, la méthode pointage dans la classe Technicien puis dans la méthode main (point d’entrée de l’exécution) on a déclaré une variable t de type Technicien à laquelle on lui a référencé une instance de type Technicien aussi comme le montre la figure.

Après l’exécution le résultat sera comme suit :


On remarque bien que la méthode redéfinie qui a été appelée.
On va faire une petite modification et voir s’il y aura du changement au niveau du résultat :


Comme vous voyez j’ai juste modifié le type de la variable t, voyons donc le résultat :

Le résultat a changé, cette fois c’est la méthode de la classe Employe qui a été exécutée.

Explication :

  • L’appel des attributs dépend de la référence et non du réfèrent.
  • Pour les méthodes déclarées static, l’appel dépend de la référence.
  • Pour les méthodes non statiques, si la méthode est présente dans les 2 classes mère et fille (il y a eu une redéfinition) dans ce cas la méthode redéfinie c’est elle qui va être appelée indépendamment de la référence sinon ça sera la méthode héritée.
  • Dans le cas d’une méthode créée uniquement dans la classe fille, pour cet exemple on la note Method() :

Classe_mére c = new Classe_fille ;

c.Method() ;

▶ Erreur de compilation : car une référence ne peut pas appelée une méthode non existante dans sa classe.

Remarque : Si la classe fille contient une méthode non déclarée ni définie dans la classe mère et que la référence de l’instance est de type super classe comme l’exemple qu’on vient de voir, dans ce cas le compilateur réclame une erreur :

b. Héritage des attributs :

On va comprendre ça à l’aide d’un bout de code :
On a ajouté un attribut à la classe Employe comme suit :

final String Immat_Societe= « 12345678 » ;

L’exécution de ce code donnera :

On peut constater que les sous classes héritent de la super classe tous les attributs et leurs valeurs, et une modification sur la valeur d’un attribut dans la classe fille n’engendre pas une modification sur le même attribut de la classe mère sauf s’il s’agit d’un attribut déclaré static.

Nous allons voir un exemple avec une classe concrète pour bien comprendre ce que je viens d’expliquer :

J’ai marqué les références par des couleurs :

  • La vert pour la référence de type Chat
  • Le jaune pour la référence de type Animal
  • Et le mauve pour la référence de type Poisson.

On va déclarer différentes variables, modifier les valeurs des variables et voir son impact sur les attributs des autres variables :


Résultat de l’exécution :

Toutes ces instructions nous ont permis de remarquer que la valeur de l’attribut Type ne change que pour la copie de l’instance qu’il a modifié (c.Type= »chat ») sinon chacune des autres instances quelle que soit la classe de la référence ont gardé leur valeurs initiales (null) avant de les modifier et après avoir modifié celle d’une autre instance.

Remarque : Pour chaque instance des classes filles on trouve que la valeur de chaque attribut Type est null pas dû à l’affectation des valeurs par défauts mais suite à l’héritage des valeurs des attributs de la classe mère.
Par contre l’attribut Nbr_Animal chaque modification faite par l’une des instances créées (Animal, Chat, Poisson) est appliquée sur une même copie partagée entre elles car cet attribut est déclaré static.

Rappel : Les attributs et les méthodes dépendent en premier lieu du type de la référence et non du réfèrent.

Remarque : On prend :
Classe_Mere a=new Classe_Fille() ;
a.Method() ;

Sachant que la Method() est une méthode déjà implémentée dans la classe mère :

Si la classe mère ne contient pas la méthode Method(), une erreur de compilation s’affiche.
Si la classe mère contient la méthode Method() et la classe fille l’a redéfinie alors la méthode redéfinie sera appelée.

Si la classe mère contient la méthode Method() et la classe fille ne l’a pas redéfinie alors la méthode héritée sera appelée.

c. La conversion (casting) :

  • La conversion implicite :

Classe_Mere a= new Classe_Fille() ;

Une telle affectation est autorisée par le compilateur et c’est lui qui fait la conversion implicitement.

  • La conversion explicite :

Dans le cas où la classe mère est une classe abstraite :

Classe_Fille a= new Classe_Mere() ;

▶ Erreur de compilation !!!

Prenons l’exemple de tout à l’heure :

Une affectation directe sans conversion d’une classe mère à une classe fille n’est pas autorisée, c’est la conversion forcée qui doit intervenir :

Donc après la conversion on peut afficher l’attribut Type pour que ça donne :

Oups !!  Ce n’est pas le résultat attendu !! Java nous dit que la conversion d’Animal en Chat n’est pas possible!

En fait, on ne peut pas affecter une instance de la classe mère  à une référence de la classe fille. La conversion forcée marche dans un seul cas :

Après la conversion explicite ça donne :

Une conversion forcée n’est acceptée que si l’instance est une instance de la classe fille, référencée par une variable de la classe mère

Une modification sur x ou w engendre une modification sur l’autre référence (w ou x) comme on le voit pour l’attribut Type car les 2 références pointent sur la même instance.

▶ L’héritage d’une interface :

Pareil pour les interfaces, une classe peut implémenter une interface mais pas le contraire, comme une interface peut hériter d’une autre interface.

Pour qu’une classe implémente une interface on utilise le mot clé implements.

Une interface peut hériter d’un nombre quelconque d’interfaces.
Pour qu’une interface hérite d’une autre interface, on utilise le mot clé extends.

Une classe peut implémenter un nombre quelconque d’interfaces.

Une classe peut en même temps hériter d’une classe et implémenter des interfaces :

Classe_Fille extends Classe_Mere implements Interface1, Interface2,…,InterfaceN

De même quand une classe implémente des interfaces elle doit redéfinir (implémenter) toutes les méthodes figurant dans toutes les interfaces (toute méthode d’une interfaces est  abstraite) sinon la classe sera considérée comme une classe abstraite et doit être déclarée ainsi (avec le mot clé abstract).

Concrétisons l’exemple des maladies :

Les  méthodes héritées doivent être redéfinies tant que la classe n’est pas abstraite.
Tout attribut doit être déclaré final, ce qui fait que toute classe ou interface qui implémente ou hérite cette interface ne peut pas modifier la valeur des attributs hérités.

Une interface ne peut pas être instanciée :

Interface_T= new Interface_T() ; ==> erreur de compilation
Elle peut seulement être une référence :
Interface_T = new Interface_T ;

 

Le tuto est terminé enfin ! C’était un peu long car ce chapitre est un peu délicat, contenant plusieurs détails auxquels il faudra bien faire attention.

Normalement on a tout vu de ce qui est une classe abstraite, qu’est-ce que c’est qu’une interface, la notion d’héritage et toutes ses règles.

Rendez-vous avec le prochain tutoriel qui sera réservé pour les boucles et les structures conditionnelles.

Merci de nous avoir fait confiance.

Je vous invite à consulter quotidiennement le site d’Odellya pour profiter des prochains tutoriels qui seront bientôt à votre disposition.

 

 

Si ce tutoriel vous a été utile, soyez généreux, pensez aux autres en le partageant dans les réseaux sociaux. Il vous suffit de cliquer sur les icônes ci-dessous.