Notion d'héritage en "orienté-objet"

Hériter... dans la vie, et en programmation

Nous héritons tous à notre naissance d'une partie du patrimoine génétique de nos parents. Malgré celà, nous développons nos propres caractéristiques en plus de celles dont nous héritons. Le principe est identique en orienté objet.

Nous pouvons par exemple définir une classe Personne.
Tous les objets issus de la classe Personne possèdent un nom et un prénom. Ce sont certains de leurs attributs.
Nous allons fournir à tous les objets de la classe Personne une méthode identifie() qui nous retourne le prénom suivi du nom de la personne.

Nous allons à présent gérer les personnes qui sont inscrites dans une bibliothèque. Ces lecteurs sont avant tout des personnes, et présentent donc toutes les caractéristiques de la classe Personne.
Mais les lecteurs possèdent tous certaines qualités (des attributs) communes (la date d'inscription, les livres empruntés) que ne possèdent pas toutes les personnes.

Nous pouvons créer une classe Lecteur, qui hérite des propriétés et des méthodes de la classe Personne, mais qui implémente des méthodes qui ne s'appliquent par exemple qu'à des personnes inscrites dans la bibliothèque. Nous remarquons de suite le gain de temps et d'espace, car nous ne sommes plus obligés de définir les méthodes et attributs qui sont déjà définis dans la classe Personne. Nous ne devons plus définir que ce qui différe de la classe dont nous héritons.

Les classes peuvent donc se présenter comme une arborescence, la racine étant en haut.

Nous dirons de la classe Personne qu'il s'agit d'une super classe, et de la classe Lecteur, qu'il s'agit d'une sous classe.

Bien que le concept d'héritage soit commun aux différents langages orientés-objet, cette page reflète plus spécifiquement l'héritage en Java.

Table des matières Haut

Remarques à propos de l'héritage

Héritage multiple

Dans certains langages tels que le C++, nous pouvons retrouver un objet qui hérite de plusieurs parents (héritage multiple), mais dans d'autres comme en Java nous ne retrouverons que des objets qui n'héritent que d'un parent maximum.
Mais plusieurs sous classes peuvent hériter d'une même super classe, il ne s'agit pas la d'héritage multiple puisque chaque sous classe n'hérite que d'une classe.

Héritage par défaut

Chaque classe en Java ne peut posséder qu'une super classe, mais elle peut avoir un nombre illimité de sous classes. Toute super classe ou sous classe que nous créons hérite automatiquement de la classe Object (la super super classe). Les attributs et les propriétés de la classe Object sont donc accessibles.

Généraliser - Spécialiser

Le plus souvent, lorsque nous effectuons un déplacement vers le haut (vers la super classe), nous généralisons. En effet, plus la classe est proche de la super classe, moins elle possède de méthodes et d'attributs spécifiques. Comme nous groupons les éléments communs dans une super-classe, nous pouvons parler de factorisation de ces éléments. Une généralisation nous permet une certaine abstraction car tout objet d'une sous-classe peut-être considéré comme un objet de sa classe parent1.

Le corollaire de cette affirmation est que plus nous nous éloignons de la super classe (plus nous descendons dans la structure), plus nous remarquons une spécialisation des classes.

Table des matières Haut

L'héritage en Java

La représentation utilisée ici est UML. Une page est consacrée aux diagrammes de classes.

Définition de la classe Personne

Personne
- nom : String
- prenom : String
+ Personne ( String nom , String prenom )
+ identifie ( ) : String
+ getNom ( ) : String
+ getPrenom ( ) : String
  1. public class Personne {
  2. private String nom;
  3. private String prenom;
  4. /**
  5. * constructeur
  6. * @param nom
  7. * @param prenom
  8. */
  9. public Personne(String nom, String prenom){
  10. this.nom = nom;
  11. this.prenom = prenom;
  12. }
  13. /**
  14. * @return donne le nom.
  15. */
  16. public String getNom() {
  17. return nom;
  18. }
  19. /**
  20. * @return donne le prenom.
  21. */
  22. public String getPrenom() {
  23. return prenom;
  24. }
  25. }

Définition de la classe Lecteur

Lecteur
- inscription : Date
- emprunts : [ ] Livre
+ Lecteur ( String nom, String prenom, Date inscription )
+ emprunte ( Livre livre ) : void
+ rapporte ( Livre livre ) : void
+ getEmprunts ( ) : [] Livre
  1. import java.util.ArrayList;
  2. import java.util.Date;
  3. public class Lecteur extends Personne {
  4. private Date inscription;
  5. private ArrayList emprunts;
  6. /**
  7. * Constructeur
  8. * @param nom
  9. * @param prenom
  10. * @param inscription
  11. */
  12. public Lecteur(String nom, String prenom, Date inscription) {
  13. super(nom, prenom);
  14. this.inscription = inscription;
  15. }
  16. /**
  17. *
  18. * @return donne la liste des livres empruntés en ce moment
  19. */
  20. public ArrayList getEmprunts() {
  21. return emprunts;
  22. }
  23. public Date getInscription() {
  24. return inscription;
  25. }
  26. /**
  27. * Ajoute le livre à ses livres empruntés en ce moment
  28. * @param livre
  29. */
  30. public void emprunte(Livre livre){
  31. emprunts.add(livre);
  32. }
  33. /**
  34. * Rapporte un livre à la bibliothèque
  35. * Enlève donc le livre de ses livres empruntés en ce moment
  36. * @param livre
  37. */
  38. public void rapporte(Livre livre){
  39. emprunts.remove(livre);
  40. }
  41. }

Que pouvons nous remarquer de particulier dans ce code ?

  1. public class Lecteur extends Personne

Nous avons utilisé le mot réservé extends qui signifie que la classe Lecteur est une  (ou classe dérivée) de Personne.

  1. public Lecteur(String nom, String prenom, Date inscription) {
  2. super(nom, prenom);
  3. this.inscription = inscription;
  4. }

Dans notre constructeur, nous recevons un nom et un prénom définissant la personne. Nous n'avons défini ces variables nulle-part dans le code de la classe Lecteur, ce sont donc des propriétés que l'objet de type Lecteur hérite de la classe Personne.

Pour cette raison, afin d'éviter toute confusion possible, nous utilisons le constructeur de la super classe (Personne) pour instancier ces valeurs.

  • Nous pouvons remarquer que l'appel au constructeur de la super classe se fait à l'aide du mot réservé super.
  • Si le constructeur de la sous-classe n'appelle pas explicitement le constructeur de la superclasse, c'est le constructeur par défaut de cette dernière qui est invoqué.
  • Dans notre constructeur de sous-classe, l'invocation du constructeur de la superclasse doit être la première instruction. Aucune instruction ne peut se placer dans un constructeur avant l'appel du super constructeur.

La date d'inscription est une des caractéristiques de notre Lecteur, nous pouvons directement affecter la valeur de la date d'inscription à la variable inscription au moment de l'appel au constructeur de Lecteur, car nous considérons qu'un lecteur n'existe qu'à partir du moment où il est inscrit dans la bibliothèque. Nous pouvons remarquer ici l'emploi du mot clé this qui désigne la classe elle-même.

Document créé le 17/04/2005, dernière modification le 07/03/2020
Source du document imprimé : https://www.gaudry.be/oriente-objet-heritage.html

L'infobrol est un site personnel dont le contenu n'engage que moi. Le texte est mis à disposition sous licence CreativeCommons(BY-NC-SA). Plus d'info sur les conditions d'utilisation et sur l'auteur.

Notes

  1.  Abstraction et héritage : Une page spécifique est consacrée à l'héritage vu comme une abstraction, et à la substitution selon B. Liskov : https://www.gaudry.be/oriente-objet-principe-substitution.html.

  2.  Unified Modeling Language : correspond à « langage de modélisation unifié » en français

  3.  UML : “Unified Modeling Language” (en français, « langage de modélisation unifié »)

Table des matières Haut