Vous êtes sur la page 1sur 10

USTHB Module POO

Faculté d’électronique et d’informatique Année 2019/2020


Département d’Informatique Section : M1-MIND

Chapitre V
Héritage et Polymorphisme

1. Introduction
Dans grand nombre d’applications, deux classes peuvent avoir des caractéristiques et des
comportements en commun, mais l'une des deux peut avoir plus de caractéristiques que l'autre et peut
aussi réagir à plus de messages (ou y réagir de manière différente). L'héritage exprime cette similarité
entre les classes en introduisant le concept de superclasse (appelée aussi classe mère ou classe de
base) et le concept de sous-classe (appelée aussi classe fille ou classe dérivée). Une classe de base
(superclasse) contient toutes les caractéristiques et comportements partagés entre les classes dérivées.
L’avantage est de pouvoir bénéficier des attributs et des méthodes de la superclasse sans avoir à les
redéfinir dans la (ou les) sous-classe (s). L’héritage constitue ainsi, l’un des principes fondamentaux
de la programmation orientée objet.

2. Expression de l’héritage en Java


En java, l’héritage est exprimé à l’aide du mot réservé « extends ». En effet, pour indiquer qu’une
classe B hérite d’une classe A sachant que A est déjà définie, on écrira en java :

class B extends A
{ // Attributs supplémentaires propres à B
// méthodes supplémentaires propres à B }

Schématiquement, pour exprimer qu’une classe B hérite d’une classe A, on aura :

A
Attributs de A

Méthodes de A

Methode 2() ; …
B

Methode
Attributs de B

Méthodes de B

Methode 2() ; …
1 S. Boukhedouma

Methode
On dira que « B hérite de A » ou « A est la superclasse de B » ou « Best une sous-classe de A » ou
encore « B étend A ». On dit aussi que le type B est un sous-type de A.
En effet, tous les attributs et toutes les méthodes définis pour A sont hérités par B qui possède des
attributs et/ou des méthodes supplémentaires non définis dans A (ou redéfinis dans B).

D’un point de vue sémantique, tout objet d’une classe dérivée (classe fille) est aussi un objet de la
classe mère (superclasse). Ainsi, pour s’assurer que le lien d’héritage est correct, il faudra examiner la
relation « est un » ou « is a » entre la superclasse et la sous-classe.

On dira par exemple que :

- Un employé est une personne ; la classe Employé peut être une sous classe de la classe
Personne.
- Un avion est un moyen de transport ; la classe Avion peut être une sous classe de la classe
MoyenTransport.
- Une capitale est une ville ; la classe Capitale peut être une sous classe de la classe Ville.
- Un ouvrage est un document ; la classe Ouvrage peut être une sous classe de la classe
Document.
- Une liste FIFO est une liste ; la classe ListeFIFO peut être une sous-classe de la classe Liste.
- Un cercle coloré est un cercle ; la classe CercleColoré peut être une sous-classe de la classe
Cercle.
- Etc…

Remarques

1. Toute classe définie dans un programme java hérite implicitement de la superclasse Object
définie dans le package java.Lang importé par défaut. Une description de la classe Object est
fournie dans l’annexe D de cet ouvrage.
2. En java, une classe ne peut hériter que d’une seule classe (autre que la classe Object), On parle
d’héritage simple. En d’autres termes, l’héritage multiple n’est pas permis en java.

Exemple 1
On suppose qu’on dispose d’une classe Produit décrivant un produit quelconque dans un supermarché.
Tout produit est décrit par une référence et un libellé.

class Produit
{String ref, libellé;
float Prix;
// constructeur
// méthodes
}

Si l’on veut définir une classe Produit-Alimentaire représentant les produits alimentaires, inutile de
créer une nouvelle classe entièrement, on peut bénéficier des attributs et des méthodes déjà définis
dans la classe Produit, et on rajoute juste les attributs/ méthodes supplémentaires nécessaire à la
classe Produit-Alimentaire. On écrira alors:

2 S. Boukhedouma
class Produit-Alimentaire extends Produit

{ // attributs supplémentaires
Date DatFab, DatExp ;
String Conditions;

// méthodes supplémentaires
}

Ainsi, la classe Produit-alimentaire qui dérive (hérite) de la classe Produit possède six attributs : Ref,
Libellé et prix hérités de la classe Date ; DatFab, DatExp pour décrire les dates de fabrication et de
péremption ainsi que l’attribut Conditions pour décrire les conditions de conservation du produit
alimentaire. Les trois derniers attributs sont propres à la classe Produit-Alimentaire.

Un objet de la classe Produit pourrait être ("Ref005", "Frottoir", 200.0)

Un objet de la classe Produit-Alimentaire pourrait être ("Ref028", "Lait", 90.00, 03-02-2017, 03-03-
2017, "Sec-frais-10°C")

A partir de la classe Produit, on pourrait dériver une autre sous-classe Produit-Détergent par exemple,
on ajoutant les spécificités d’un produit détergent comme par exemple : mode d’emploi et précautions.

Exemple 2
Voici la description d’une classe Arbre.

class Arbre
{String Nom, Espece, Zone, CondVie;
// constructeur
// méthodes
String getNom()
{return Nom; }
…}

On pourra dériver par exemple, une classe ArbreFruitier à partir de la classe Arbre, en ajoutant des
attributs et des méthodes spécifiques à cette sous-classe

class ArbreFruitier extends Arbre


{String Nomfruit, Formefruit; Color Cfruit;
// constructeur
// méthodes
String getForme() {return Formefruit; }
…}

Ainsi, la classe ArbreFruitier qui dérive de la classe Arbre possède sept attributs : Nom, Espece, …
Formefruit et Cfruit. Elle peut utiliser les méthodes de la classe Arbre (exemple getNom()…) et ses
propres méthodes telles que getForme() …

Remarque : On peut interdire qu’une classe soit étendue, il suffit de lui attribuer le modificateur
« final », en écrivant :
final class A
{ // attributs et méthodes de A}

Il est interdit ensuite d’écrire class B extends A

3 S. Boukhedouma
3. Chaînage des constructeurs

Tout constructeur d’une classe autre que la classe Object fait appel soit à un constructeur de sa
superclasse, soit à un autre constructeur de la même classe. L’appel au constructeur de la superclasse
se fait à l’aide du mot réservé «super» considéré comme un appel de méthode. Un appel au
constructeur de la même classe se fait à l’aide du mot réservé « this » considéré aussi comme un appel
de méthode.

Exemple : Reprenons l’exemple de Produit et Produit-Alimentaire pour y ajouter un constructeur

class Produit
{String ref, libellé;
float Prix;
// constructeur
Produit ( String ref, String lib, float px)
{ this.ref = ref ; libellé = lib ; prix = px ; }
// autres méthodes
}

class Produit-Alimentaire extends Produit

{ // attributs supplémentaires
Date DatFab, DatExp ;
String Conditions;
// constructeur
Produit-Alimentaire (String ref, String lib, float px, Date DF, Date DE,
String cond)
{ super (ref, lib, px) ; /* appel du constructeur de la
superclasse pour initialiser les attributs ref, libellé et prix */
DatFab = DF ; DatExp = DE ; Conditions = cond ;
} // initialisation des autres attributs
// autres méthodes
}

Remarques

1. L’appel avec le mot réservé super doit être obligatoirement en première ligne du constructeur.
2. Si l’appel avec super n’existe pas dans le constructeur de la classe dérivée, le compilateur en
ajoute un par défaut avec l’instruction super(); sans paramètres. Et, si dans la superclasse, il a
été défini un constructeur avec paramètres seulement, le compilateur génère une erreur.

4. Accès aux attributs et méthodes de la superclasse


Pour accéder aux attributs/ méthodes de la superclasse à l’intérieur de la sous-classe, on utilise le mot
réservé super suivi du nom du champ (attribut ou méthode) selon une notation pointée. L’utilisation
du mot super n’est pas obligatoire s’il n’y a pas de redéfinition de champs (attributs ou méthodes)
dans la sous-classe.

Exemple : on reprend l’exemple de Produit et Produit-Alimentaire pour y ajouter une méthode


Modifier qui modifie le libellé, le prix, date de fabrication et péremption.

4 S. Boukhedouma
class Produit
{String ref, libellé;
float Prix ;
// constructeur
Produit ( String ref, String lib, float px)
{ this.ref = ref ; libellé = lib ; prix = px ; }

// méthode Modifier
void Modifier (String newlib, float newpx)
{ libellé = newlib ; prix = newpx ; }
}
class Produit-Alimentaire extends Produit

{ // attributs supplémentaires
Date DatFab, DatExp ;
String Conditions;
// constructeur
Produit-Alimentaire (String ref, String lib, float px, Date DF, Date DE,
String cond)
{ super (ref, lib, px) ; /* appel du constructeur de la
superclasse pour initialiser les attributs ref, libellé et prix */
DatFab = DF ; DatExp = DE ; Conditions = cond ;
} // initialisation des autres attributs
// méthode Modifier
void Modifier (String newlib, float newpx, Date newDF, Date newDE)
{ super.Modifier (newlib, newpx);
/* appel de la méthode Modifier de la classe Produit */
DatFab = newDF ; DatExp = newDE ;}
}

Question : si l’on veut attribuer les modificateurs de visibilité (public, private, protected) aux
différents champs dans les classes Produit et Produit-Alimentaire, lesquels pourrait-on utiliser?
Expliquez.

5. Redéfinir une méthode

Lorsqu’une classe B étend une classe A, il est possible de redéfinir une méthode de la classe A dans la
classe B dérivée de A avec exactement la même signature (même nom, mêmes types et ordre des
paramètres et même type de retour). Ceci constitue un des aspects principaux liés à l’héritage dans les
langages orientés objets : il s’agit du polymorphisme qui sera explicité plus loin dans ce chapitre. La
méthode redéfinie est dite alors méthode polymorphe.

Exemple 1: on reprend l’exemple de Produit et Produit-Alimentaire pour ajouter une méthode


Afficher.

class Produit
{String ref, libellé;
float Prix ;
// constructeur
Produit ( String ref, String lib, float px)
{ this.ref = ref ; libellé = lib ; prix = px ; }

// méthode Modifier

5 S. Boukhedouma
void Modifier (String newlib, float newpx)
{ libellé = newlib ; prix = newpx ; }

// méthode Afficher
void Afficher ()
{ System.out.println (" La description du produit est :" + ref + "/ " + libellé + "/ " prix ) ;}
}

class Produit-Alimentaire extends Produit

{ // attributs supplémentaires
Date DatFab, DatExp ;
String Conditions;
// constructeur
// méthode Modifier

// méthode Afficher
void Afficher ()
{ // on utilise les attributs ref , libellé, prix définis dans la superclasse
System.out.println (" la description du produit est :" + super.ref + "/ " + super.libellé
+ "/" + super.prix ) ;
System.out.println (" Date fabrication :") ; DatFab.Afficher() ; /*on appelle une
méthode Afficher()supposée définie dans la classe Date pour afficher une date*/
System.out.println (" Date péremption :") ; DatExp.Afficher() ;
System.out.println (" Conditions de conservation :" + Conditions) ;

Remarque : la méthode Modifier n’est pas polymorphe car elle ne possède pas le même prototype
dans les deux classes. Par contre la méthode Afficher est polymorphe, elle a été redéfinie avec le
même prototype dans la classe Produit-Alimentaire.

On aurait pu écrire la méthode Afficher autrement :


void Afficher()
{ // on invoque la méthode Afficher de la superclasse
super.Afficher() ;
System.out.println (" Date fabrication :") ; DatFab.Afficher() ;
/*on appelle une méthode Afficher()supposée définie dans la classe Date pour afficher une date*/
System.out.println (" Date péremption :") ; DatExp.Afficher() ;
System.out.println (" Conditions de conservation :" + Conditions) ;

class ProgProduit
{ public static void main (String arg[ ])
{ Date DF = new Date (3, 2, 2017) ; Date DF = new Date (3, 3, 2017) ;
Produit-Alimentaire P = new Produit-Alimentaire ("Ref028", "Lait", 90.0, DF, DE, "Sec-
frais- 10°C") ;

System.out.println ("Il s’agit du produit suivant :") ; P.Afficher();


System.out.println (" on va modifier le produit ") ;
P.Modifier ("Lait entier", 110.0) ;
System.out.println ("Il s’agit maintenant du produit:"); P.Afficher();
}
}

6 S. Boukhedouma
6. Redéfinir la méthode toString de la classe Object

La méthode toString définie dans la superclasse Object permet de renvoyer une description de
n’importe quel objet sous forme d’une chaine de caractères (String) comportant le nom de la classe à
laquelle appartient l’objet en plus de la référence de l’objet en mémoire sous format exadécimal par
exemple pour l’objet P de type Produit-Alimentaire , l’appel de la méthode P.toString() ; renvoie une
description qui ressemblerait à Produit-Alimentaire@006352.
Cette méthode est alors toujours redéfinie pour renvoyer une description textuelle complète de l’objet
telle que le programmeur le veut.

Exemple: Redéfinition de la méthode toString de la superclasse Object


class Fruit
{String nom;
String vitamine;
String couleur;
//constructeur
Fruit (String n, String v, String c)
{ nom = n; vitamine = v; couleur = c;}
// autre méthode

String toString() // redéfinition de la méthode toString de la classe Object


{return (" Il s’agit d’un fruit appelé " + nom+ " de couleur "+
couleur +"riche en " + vitamine ) ; }
}

class Prog_Fruit
{ public static void main (String arg[ ])
{ Fruit F = new Fruit ("Fraise", "Vitamine C", "rouge");
System.out.println (F.toString()); // ou alors System.out.println (F); }
}

Dans ce cas, l’affichage suite à l’appel de la méthode toString (redéfinie dans la classe Fruit)
donnera : Il s’agit d’un fruit appelé Fraise de couleur rouge riche en Vitamine C.

Remarques

- La méthode equals définie dans la classe Object est aussi souvent redéfinie pour vérifier
l’égalité de deux objets d’une même classe donnée car à l’origine cette méthode vérifie
l’égalité des références (le même objet en mémoire).
- Depuis Java 5, on peut annoter par @Override les redéfinitions de méthodes. Ceci est très
utile pour repérer des fautes de frappe dans le nom de la méthode : le compilateur envoie un
message d’erreur si la méthode ne redéfinit aucune méthode de la superclasse.
- Si une méthode est déclarée final dans une classe, il est impossible de la redéfinir dans les
classes dérivées.

Exercice
1. On demande d’implémenter une classe qui décrit les formes géométriques régulières. On
suppose qu’une forme possède un centre et une couleur.

7 S. Boukhedouma
2. Implémenter deux classes Carré et Cercle en donnant les spécificités de chacune d’elles. Les
traitements à implémenter doivent permettre entre autres, de déplacer, redimensionner,
calculer le périmètre et la surface de la forme.

7. Transtypage et Polymorphisme
Le transtypage (conversion de type ou cast en anglais) consiste à modifier le type d'une variable ou
d'une expression. Par exemple, il est possible transtyper un int en double (voir chapitre I).

 Transtypage de références d'objets


Il est possible de convertir un objet d'une classe en un objet d'une autre classe si les classes sont liées
par l'héritage (on utilise le mot objet par abus de langage : ce sont les références aux objets et non les
objets eux-mêmes qui sont transtypés).
Comme pour les types primitifs, il existe deux types de transtypage : implicite et explicite.

a- Transtypage implicite (classe fille vers classe mère)


Il est toujours possible d'utiliser une référence de la classe mère pour désigner un objet d'une classe
dérivée (fille, petite-fille et toute la descendance). Il y a alors transtypage implicite (appelé aussi
upcasting) de la classe fille vers la classe mère. Cela est logique si on se dit qu'un objet dérivé est un
objet de base.

Exemple : conversion d’une instance de la classe « Produit-Alimentaire » vers la classe « Produit »

Produit-Alimentaire PA ;
Produit P ;
PA= new Produit-Alimentaire (…) ;
// on crée un objet PA de la classe Produit-Alimentaire
P = PA ;
// une référence de Produit peut référencer un objet de type Produit-Alimentaire

Il faut noter, tout de même que P n’aura pas accès aux champs spécifiques à la classe fille Produit-
Alimentaire. Il faudra pour cela effectuer un transtypage explicite de Produit vers Produit-Alimentaire.

b- Transtypage explicite (classe mère vers classe fille)


Le transtypage explicite (appelé aussi downcasting) des références est utilisé pour convertir le type
d'une référence dans un type dérivé. C'est logique si l'on se dit qu'un objet d'une classe mère n'est pas
un objet de ses classes filles (un Produit n’est pas forcément un Produit-Alimentaire).
Par exemple, si l'on veut utiliser la référence P (qui est du type Produit mais qui désigne un produit-
alimentaire) pour invoquer une méthode spécifique de la classe Produit-Alimentaire, il faudrait réaliser
un transtypage explicite de P en Produit-Alimentaire.

Exemple

Date DF ;
DF = (Produit-Alimentaire)P.getDateFab() ;
Si on avait écrit DF = P. getDateFab() ; le compilateur génère une erreur car P n’a pas accès à une
méthode spécifique de la classe Produit-Alimentaire.

8 S. Boukhedouma
Remarque
Un transtypage explicite n'est pas toujours possible, même si les classes ont un lien de parenté. C'est
au programmeur de vérifier que son transtypage n'engendrera pas d'erreur, c'est-à-dire que l'objet est
bien effectivement du type dans lequel on transtype la référence.

Exemple de transtypage explicite impossible : créer un objet Produit à partir d'une référence de type
Produit-Alimentaire
Produit-Alimentaire P = new Produit ("Ref008", "XXX", 1250.0); // impossible

Le downcasting ne permet pas de convertir une instance d’une classe donnée en une instance
d’une sous-classe !
class A {…}
class B extends A {…}
A a = new A();
B b = (B) a; ce code compile, mais plantera à l’exécution

c- Polymorphisme et liaison dynamique


La liaison dynamique est un aspect important du polymorphisme, lié plus exactement aux méthodes
polymorphes (méthodes redéfinies dans les classes filles). La liaison dynamique un mécanisme qui
permet de déterminer la méthode à exécuter lors de son invocation sur un objet, au moment de
l’exécution du programme et non au moment de la compilation (liaison statique) :
A l'invocation d'une méthode, le choix de l'implémentation à exécuter ne se fait pas en fonction du
type déclaré de la référence à l'objet, mais en fonction du type réel de l'objet. Le type réel de l’objet est
celui qui a été utilisé pour sa création (new <type >…).

Exercice
On considère une superclasse A et deux classes dérivées B et C.

public class A { ... // méthode Meth()}


public class B extends A { ... // méthode Meth() redéfinie dans B}
public class C extends A { ... // méthode Meth() redéfinie dans C}

public class Test


{public static void main (String args [])
{ // on crée un tableau tab de trois objets de type A
A tab [] = new A[3]
tab [0] = new B(…); // on crée trois objets de type B, A, C stockés dans tab
tab [1] = new A(…);
tab [2] = new C(…);
tab[0].Meth();
tab[1].Meth();
tab[2].Meth();
// on effectue un cast explicite
(A)tab[0]. Meth() ; (B) tab[1]. Meth() ; (C) tab[1].Meth() ; }

Question: Quelle méthode Meth() est exécutée à chaque invocation ? Expliquez.

9 S. Boukhedouma
8. L’opérateur instanceof et la méthode getClass

On peut utiliser le mot-clé instanceof pour savoir si une référence est d’une classe donnée ou d’une
de ses sous-classes.
Si a est une référence de type B alors l’expression a instanceof A renvoie true si B est une sous-classe
de A et false sinon.
class A {…}
class B extends A {…}

a = new B() ;
if (a instanceof A) {
System.out.print ("a référence un A,") ;
System.out.println ("ou une sous classe de A."); }

Remarque
Attention, l’utilisation de instanceof est souvent la marque d’un défaut de conception et va à
l’encontre du polymorphisme.

La méthode getClass définie dans java.lang.Class permet de retourner la classe effective de l’objet (et
non la superclasse) précédée du nom du package. On écrira par exemple a.getClass() ; Les méthode
getName(), getSimpleName() permettent aussi de retourner le nom de la classe à laquelle appartient
l’objet.
D’autres méthodes de la classe Class comme getDeclaredMethods() ; getDeclaredFields() ;
getDeclaredConstructors(); permettent de retourner le contenu d’une classe (l’ensemble des
méthodes, constructeurs, attributs, …).

9. Cacher une méthode de classe


Une méthode de classe (déclarée static) d’une classe mère A peut être cachée dans une sous-classe B
dérivée de A, en la « redéfinissant » dans la classe B. On dit qu’on cache la méthode (en anglais hide).
Contrairement aux méthodes d’instance, les appels aux méthodes de classe sont résolus statiquement à
la compilation (pas de liaison dynamique).
Si une instance est utilisée pour l’invocation, la classe utilisée sera celle du type déclaré et non du type
réel de l’objet.
En résumé, il n’y a pas de polymorphisme sur les méthodes de classe (les méthodes static)

10. Avantages de l’héritage


 L'héritage supprime, en grande partie, les redondances dans le code des applications. Une fois
la hiérarchie de classes bien établie, on localise en un point unique les sections de code
identiques (celles-ci restent à tout moment accessibles grâce au mot clé super).

 Possibilité de rajouter facilement une classe, et ce à moindre coût, puisque l'on peut réutiliser
le code des classes parentes.

 Si un comportement n’a pas été modélisé dans une classe donnée, et qu’il est nécessaire de le
rajouter, une fois l'opération terminée, ce comportement sera directement utilisable dans
l'ensemble des sous-classes de la classe en question

10 S. Boukhedouma

Vous aimerez peut-être aussi