Polymorphisme
**Objectifs spécifiques :**
1. Comprendre le concept de polymorphisme 2. Intégrer la notion de surclassement (upcasting) et de ligature dynamique 3. Saisir la notion de conversion explicite
**Eléments de contenu :**
* Concept de polymorphisme * Surclassement (**upcasting**) * Ligature dynamique. * Conversion explicite (**Downcasting**) * Conversion des arguments effectifs
**Volume horaire :** 3h00
| | | | --- | --- | |  |  |
1. Concept de polymorphisme
* Le terme polymorphisme décrit la caractéristique d'un élément qui peut prendre plusieurs formes. * **"**Le **polymorphisme** constitue la troisième caractéristique essentielle d'un langage orienté objet après l'abstraction des données (encapsulation) et l'héritage"[[1]](#footnote-1). C’est un concept extrêmement puissant en POO qui permet de manipuler des objets sans en connaître tout à fait le type tout en se basant sur la relation d’héritage. * Le polymorphisme en Java, se traduit par : + Le **surclassement** : La **compatibilité** **ascendante** entre un type dérivée et un type ascendant. + La **ligature dynamique** des méthodes, dans le sens où il permet d’obtenir le comportement adapté à chaque type d’objet, sans avoir besoin de tester sa nature de quelque façon que ce soit.
| | | | --- | --- | |  |  |
* La notion de polymorphisme peut être généralisée dans le cas de dérivations successives. * Le polymorphisme se base sur cette affirmation : un objet a comme type non seulement sa classe mais aussi n’importe quelle classe dérivée. * si dans le même contexte d’héritage, on a à la fois une redéfinition et une surcharge de la même méthode, les règles de polymorphisme deviennent de plus en plus compliquées.
1. Surclassement (upcasting)
* **Une classe B qui hérite de la classe A peut être vue comme un** **sous-type (sous ensemble) du type défini par la classe A.** * Tout objet instance de la classe B peut être aussi vu comme une instance de la classe A. * Cette relation est directement supportée par le langage JAVA : + à une référence déclarée de type A il est possible d'affecter une valeur qui est une référence vers un objet de type B (**surclassement** ou **upcasting**)
**class** A {
**void** m1(){ System.***out***.println("Objet de classe A");}
}
**class** B **extends** A {
**void** m2(){ System.***out***.println("Objet de classe B");}
}
On pourrait tout à fait écrire le code suivant dans la classe Test
A a = **new** B();
a.m1();//affiche objet de classe A
A travers ce court exemple vous avez vu le premier concept du polymorphisme : la compatibilité ascendante. En effet, vous avez manipulé **un objet de type *A* via une référence de type *B. A*** *étant la classe mère de* ***B***.
**Exemple**
| | | | --- | --- | |  | **EtudiantSportif** est un **Etudiant**. L’ensemble des étudiants sportifs est inclus dans l’ensemble des étudiants. Etudiant e ; e=**new** EtudiantSportif(…) ; |
* + Plus généralement à une référence d'un type donné, il est possible d'affecter une valeur qui correspond à une référence vers un objet dont le type effectif est n'importe quelle sous-classe directe ou indirecte du type de la référence.

* Lorsqu'un objet est "sur-classé" il est vu comme un objet du type de la référence utilisée pour le désigner + Ses fonctionnalités sont alors **restreintes** à celles proposées par la classe du type de la référence
**class** A {
**void** m1(){ System.***out***.println("Objet de classe A");}
}
**class** B **extends** A {
**void** m2(){ System.***out***.println("Objet de classe B");}
}
A a = **new** B();
a.m1();
a.m2();//faux car le type de référence de l'objet « a » est la classe A. Donc l'objet « a » n'a pas accès à la méthode m2 de la classe B
**Exemple**

EtudiantSportif es;
es = **new** EtudiantSportif("DUPONT","Jean",25,"Badminton");
Etudiant e;
e = es; // surclassement (upcasting)
es.bonusSportif();
e.bonusSportif();//Faux car il y a restriction, e n’a accès qu’aux méthodes de la classe Etudiant.
1. Ligature dynamique
* Dans le chapitre portant sur l’héritage nous avons présenté les concepts de redéfinition de méthodes. La prise en compte du polymorphisme va compliquer la redéfinition. * Les messages sont résolus **à l’exécution** + la méthode exécutée est déterminée à l'exécution (run-time) et non pas à la compilation + à cet instant le type exact de l'objet qui reçoit le message est connu - la méthode définie pour le type réel de l’objet recevant le message est appelée (et non pas celle définie pour son type déclaré). * Ce mécanisme est désigné sous le terme de **lien-dynamique** (*dynamic binding*, *latebinding* ou *run-time binding*)

**Exemple :**
**public** **class** Etudiant {
String nom, prénom;
**int** age;
**public** Etudiant(String nom, String prénom, **int** age) {
**super**();
**this**.nom = nom;
Publicité
**this**.prénom = prénom;
**this**.age = age;
}
**public** **void** affiche(){System.***out***.println("je suis étudiant");}
}
**public** **class** EtudiantSportif **extends** Etudiant{
String sport;
**public** EtudiantSportif(String nom, String prénom, **int** age, String sp) {
**super**(nom, prénom, age);
sport=sp;
// **TODO** Auto-generated constructor stub
}
**public** **void** affiche(){
**super**.affiche();
System.***out***.println("je suis sportif, je pratique le "+**this**.sport);
}
}
…
Etudiant e=**new** EtudiantSportif("Somrani","Mohamed",20,"foot");
e.affiche();//Quel est le résultat ??
Que va afficher l’appel à la méthode affiche de l’objet e : à la méthode « affiche() de la classe Etudiant ou à celle de la classe « EtudiantSportif »
**Exécution**
je suis étudiant
je suis sportif, je pratique le foot
| | | | --- | --- | |  | Lorsqu’une méthode d’un objet est accédée au travers d’une référence « surclassée » , c’est la méthode telle qu’elle est définie au niveau de la **classe effective** de l’objet qui est en fait invoquée et exécutée. |
1. Application du polymorphisme
//Classe de base
**class** graphique {
**private** **int** x, y;
**public** graphique ( **int** x, **int** y) { **this**.x = x ; **this**.y = y ;}
**public** **void** identifie () {
System.***out***.println ("Je suis une forme géometrique") ;}
**public** **void** affiche () {
**this**.identifie() ;
System.***out***.println ("Le centre de l’objet se trouve dans :" + x + "et" + y) ;}
**double** surface () {**return** (0) ;}
} // fin de la classe graphique
//Classe dérivée1
**class** Cercle **extends** graphique {
**private** **double** rayon =1 ;
**public** Cercle ( **int** x, **int** y, **double** r) {**super**(x,y) ;rayon = r ;}
**public** **void** identifie () {
System.***out***.println ("Je suis un cercle");}
**double** surface (){
**return** ( rayon \* rayon \* 3.14) ;} }
//Classe dérivée2
**class** Rectangle **extends** graphique {
**private** **int** larg, longueur ;
Rectangle ( **int** x, **int** y, **int** l1, **int** l2){
**super** (x,y) ;
longueur = l1 ; larg = l2 ;}
**double** surface () {**return** (longueur\*larg) ;}
**public** **void** identifie() {
System.***out***.println("Je suis un rectangle ") ;}
}// fin de la classe Rectangle
Publicité
//Classe de test
**class** test\_poly {
**public** **static** **void** main (String [] args) {
graphique g = **new** graphique (3,7);
g.identifie ();
g= **new** Cercle (4,8,10) ;
//compatibilité entre le type de la classe et de la classe dérivéé
g.identifie() ;
g= **new** Rectangle (7,9,10,3) ;
//compatibilité entre le type de la classe et de la classe dérivéé
g.identifie () ;
}
}
**Résultat de l’exécution**
Je suis une forme géometrique
Je suis un cercle
Je suis un rectangle
**Interprétation**
Le même identificateur « g » est initialisé dans la 1ère instruction avec une référence de type « graphique », puis on a changé la référence de cette variable dans l’instruction3 en lui affectant une référence de type « Cercle », puis dans l’instruction 5, on a changé sa référence avec une référence de classe dérivée « Rectangle ».
Ces affectations confirment la compatibilité entre un type d’une classe de base et la référence d’une classe dérivée.
L’autre point qui semble plus important c’est la ligature dynamique des méthodes, dans le sens où le résultat de la méthode « identifie » a changé dans chaque appel selon le type effectif de la variable « g ».
**Exemple 2 : Tableau des objets**
Dans cet exemple, on va exploiter les possibilités de polymorphisme pour créer un tableau hétérogène d’objets, c’est à dire dans lequel les éléments peuvent être de types différents.
**class** test\_poly2 {
**public** **static** **void** main (String [] args) {
graphique [] tab = **new** graphique [6];
tab[0] = **new** graphique (3,2);
tab[1] = **new** Cercle (10,7,3) ;
tab [2] = **new** Rectangle (4,7,8,6) ;
tab [3] = **new** graphique (8,10);
tab[4] = **new** Cercle (8,5,3) ;
tab[5] = **new** Rectangle (10,17,3,8) ;
**for** (i=0 ; i <=5 ; i++) {
tab[i].identifie();}
}
}
**Résultat de l’exécution**
Je suis une forme géométrique Le centre de l'objet se trouve dans : 3 et 2
Je suis un cercle Le centre de l'objet se trouve dans : 10 et 7
Je suis un rectangle Le centre de l'objet se trouve dans : 4 et 7
Je suis une forme géométrique Le centre de l'objet se trouve dans : 8 et 10
**Exemple 3 :**
**class** test\_poly3 {
**public** **static** **void** main (String [] args) {
graphique d;
**int** c= Integer.*parseInt*(args [0]) ;
**switch** (c) {
**case** 0 : d=**new** Cercle (10,10,10) ; **break**;
**case** 1 : d=**new** Rectangle (10,10,10,10) ; **break**;
**case** 2 : d= **new** graphique (10,10); **break**;
**default**: d= **new** graphique (0,0);
}
d.identifie () ;
System.***out***.println ("Surface ="+ d.surface()) ;
}}
Publicité
**Résultat de l’exécution :**
Le message affiché est en fonction de l’argument introduit dans la commande d’exécution.
1. Conversion des arguments effectifs
**Exemple 1 :**
**class** A{
**public** **void** identifie() {
System.***out***.println ("Objet de type A") ; }
}
**class** B **extends** A{
// pas de redéfinition de identifie ici
}
**class** T{
**static** **void** f(A a ) {
a.identifie(); }
}
…..
A a = **new** A();
B b = **new** B ();
T.f(a); // OK Appel usuel; il affiche “objet de type A”
T.f(b) ; /\*OK une référence à un objet de type B est compatible avec une référence à un objet de type A. L’appel a.identifie() affiche : « Objet de type A » \*/
**Exemple 2 :**
On va juste modifier le corps de la classe B par une redéfinition de la méthode « identifie »
**class** B {
**public** **void** identifie () {
System.out.println (« Objet de type B ») ;}
}
A a = **new** A(); B b = **new** B ();
T.f (b) ;
/\*OK une référence à un objet de type B est compatible avec une référence à un objet de type A. L’appel a.identifie() affiche : « Objet de type B »\*/
Cet exemple, montre que comme dans les situations d’affectation, la conversion implicite d’un type dérivé dans un type de base n’est pas dégradante puisque grâce au polymorphisme, c’est bien le type de l’objet référencé qui intervient.
***Remarque :***
Dans le cas où la méthode f est surdefinie, il y a encore des règles plus compliquées lors de la conversion implicite des types de paramètres.
1. Conversions explicites (downcasting)
Compatibilité entre référence à un objet d’un type donné et une référence à un objet d’un type ascendant. Dans le sens inverse, la compatibilité n’est pas implicite.
**Exemple** :
**class** graphique {…….}
**class** Cercle {……..}
…..
graphique g,g1 ;
Cercle c,c1 ;
C = **new** graphique (….) ; //Erreur de compilation
g= **new** Cercle (…) //Juste
g1 = **new** graphique (….) ; //évident
c1= **new** Cercle(…) ; //évident
c1 = g1 //faux (Conversion implicite illégale)
g1= c1 ; //juste ; (conversion implicite légale)
Il faut réaliser une conversion explicite **c1 = (Cercle) g1** ;
Toutefois cette syntaxe qui est acceptée à la compilation peut être refusée à l’exécution et le traitement peut être arrêtée si il n ‘y a pas compatibilité effective entre les types.
**Exercice : Upcasting/Downcasting**


1. *Bruce Eckel "Thinking in JAVA"* [↑](#footnote-ref-1)