L'héritage

 

1.     INTRODUCTION

 

Exemple :

 

 

classe Véhicule                 //n’importe quel objet roulant

privé:

noveh : entier

            couleur : chaîne de caractères

poids : réel                    //poids du véhicule 

public:

PROCEDURE initialiser (données : n : entier, c : chaîne de caractères, p : réel)      

            DEBUT

              noveh ß n

                        couleur ß c

              poids ß p

FIN

 

FONCTION donnercouleur() : chaîne de caractères

            DEBUT

                        Retourner couleur

            FIN

 

FONCTION donnerpoids() : réel

DEBUT

                        Retourner poids

            FIN

Fclasse

 

classe Voiture :   hérite de Véhicule    //classe pour les voitures

 

Fclasse

 

 

Cette déclaration stipule que la classe "Voiture" dérive de "Véhicule", c’est à dire qu’elle en possède toutes les caractéristiques. En clair, une" Voiture " est un "Véhicule". La classe "Véhicule" est dite "classe de base", " Voiture " est appelée une "classe dérivée de Véhicule".

Un objet de type « Voiture » possédera donc les données membres noveh, couleur, poids et toutes les méthodes de « Véhicule » (sauf les constructeurs (cf. codage en Java)) :

procédure initialiser(données: n : entier, c : chaîne de caractères, p : réel)         

fonction donnercouleur() : chaîne de caractères  

fonction donnerpoids() : réel

 


 

2.     UTILISATION DES MEMBRES « HERITES »

 

Nous venons de voir que la classe dérivée "hérite" des membres définis dans la classe de base (données et méthodes).

 

Programme Test1

DEBUT

VAR voit1 : Voiture

          pds : réel

          //initialisation de voit1

          voit1.initialiser (1, « rouge », 1500)

          Afficher « Couleur : », voit1.couleur  //interdit tout comme avec un

                                                                                              //objet de type Véhicule

          pds ß voit1.donnerpoids()

          …

FIN

 

Les membres publics méthodes ou données de la classe de base sont utilisables avec un objet de la classe dérivée tout comme avec un objet de la classe de base.

 

Remarque : en Java, l'héritage multiple n'existe pas, seul l'héritage simple est possible, c'est à dire qu'une classe ne peut être dérivée de plusieurs classes.

 

 

3.     SPECIALISATION

 

Pour le moment, la classe "Voiture" est identique à la classe " Véhicule" et ne présente donc aucun intérêt. La spécialisation consiste à ajouter des méthodes ou des données à la classe dérivée (on dit que l’on spécialise la classe de base).

 

Exemple :

classe Voiture : hérite de Véhicule

privé :

          nbpassagers :entier

          métallisé : booléen

public :

          PROCEDURE initialiser(données : n : entier, c : chaîne de caractères, p : réel, gens : entier, metal : booléen)

          DEBUT

                        initialiser(n, c, p)

                        nbpassagers ß gens

                        métallisé ß métal

          FIN

         

          FONCTION donnerpassagers() : entier

          DEBUT

                        Retourner nbpassagers

          FIN

 

          FONCTION donnercouleur() : chaîne de caractères

          DEBUT

                        SI métallisé

                        ALORS

                                   texte ß " métallisé"

                        SINON

                                   texte ß " mat"

                        FSI

                        Retourner Véhicule::donnercouleur() + texte    // Il est impossible de retourner

                                                                                                  // couleur + texte                            FIN

         

Fclasse

 

Chaque « Voiture » possède 5 membres "données" : noveh, couleur, poids, nbpassagers, métallisé et toutes les méthodes des 2 classes :

procédure initialiser(données : n : entier, c : chaîne de caractères, p : réel)    //initialisation de no, couleur et poids  

procédure  initialiser(données : n : entier, c : chaîne de caractères, gens :entier, métal : booléen )  //initialisation de toutes les données  

fonction donnercouleur() : chaîne de caractères   //fonction de la classe Véhicule

fonction donnercouleur() : chaîne de caractères   //fonction de la classe Voiture

fonction donnerpoids() : réel

fonction donnerpassagers() : entier

 

Il est donc possible d’attribuer le même nom à des membres de la classe héritée et à des membres de la classe de base.

 

Programme Test2

DEBUT

VAR véh1, véh2 : Véhicule

          voit1, voit2 : Voiture

          pds : réel

          couleur : chaîne de caractères

          nbpas : entier

         

          voit1.initialiser (1, "bleu", 1000)                   // Appel à initialiser de Véhicule

          voit2.initialiser (2, "rouge", 1500, 7, Vrai)   // Appel à initialiser de Voiture

          véh1.initialiser (1, "vert", 800)                      // Appel à initialiser de Véhicule

          véh2.initialiser (2, "jaune", 900, 7, Vrai)                 // Erreur

 

          pds ß voit1.donnerpoids()                // Appel à donnerpoids de Véhicule

          pds ß voit2.donnerpoids()                // Appel à donnerpoids de Véhicule

          pds ß véh1.donnerpoids()                 // Appel à donnerpoids de Véhicule

 

          couleur ß voit1.donnercouleur()       // Appel à donnercouleur de Voiture

          couleur ß voit2.donnercouleur()       // Appel à donnercouleur de Voiture

          couleur ß véh1.donnercouleur()       // Appel à donnercouleur de Véhicule

                                                                      

          nbpas ß voit1.donnerpassagers()      // Appel à donnerpassagers

                                                                       // Peut rendre n'importe quoi car non

                                                                       // initialisé (c'est ce que l'on considère en

                                                                       // pseudo-code)

          nbpas ß voit2.donnerpassagers()      // Appel à donnerpassagers

                                                                       // Place 7 dans nbpas

          nbpas ß véh1.donnerpassagers()      // Erreur

                                                                      

          …

FIN

 

Remarque : il est impossible d'appliquer une méthode ou d'utiliser une donnée de la classe dérivée avec un objet de la classe de base (on verra qu’on peut le faire en Java avec une référence de la classe de base si la référence désigne un objet de la classe dérivée).

 

 

Règles pour les méthodes publiques :

 

- Si la méthode n’existe pas dans la classe dérivée, il y a appel de la méthode de la classe de base.

 

Exemple : ………………………………..

 

 

- S'il y a une méthode de même nom mais avec une signature différente, l’appel se fait suivant la concordance entre les paramètres effectifs et les paramètres formels. On parle de surcharge comme c'est le cas à l'intérieur d'une même classe.

 

Exemple : ……………………………..

 

 

- Si la signature est la même, on dit alors que la méthode est redéfinie dans la classe dérivéeou qu’elle outrepasse la méthode de la classe de base. La méthode de la classe de l’objet d’appel s’applique, on dit qu’elle cache la méthode de la classe de base. On dit également qu'il y a masquage de la méthode de la classe de base.

 

Exemple : ……………………………..

 

Pour appeler explicitement la méthode de la classe de base (ceci sera possible uniquement à l'intérieur de la classe héritée et non dans les autres programmes) avec un objet de la classe dérivée, il faudra indiquer NomClasseBase::NomMéthode().

 

Exemple : ……………………………..

 

C’est évidemment la méthode de la classe dérivée qui est considérée quand la méthode n’existe pas dans la classe de base (dans ce cas, Java ne regarde même pas dans la classe de base).

 

Exemple : ……………………………..

 

 

Règles pour les données publiques :

 

-          Si la donnée n'existe pas dans la classe dérivée, c'est la donnée de la classe de base qui est considérée.

-          Si la donnée est redéfinie, c'est la donnée de la classe de l'objet d'appel qui est considérée à moins d'indiquer NomClasseBase::NomDonnée.

-          C’est évidemment la donnée de la classe dérivée qui est considérée quand la donnée n’existe pas dans la classe de base (dans ce cas, Java ne regarde même pas dans la classe de base).

 

Règles pour les données et méthodes privées

 

Les membres privés de la classe de base sont inaccessibles à l'intérieur des méthodes de la classe dérivée comme ils le sont de façon générale à l'extérieur de la classe de base.

 

Par exemple, il ne sera pas possible de désigner couleur dans une méthode de la classe Voiture car c’est un membre privé de « Véhicule » ; il faudra donc utiliser la fonction donnercouleur de la classe « Véhicule »pour récupérer la valeur de couleur.

 

Remarque :

Toutes les règles citées précédemment sont également valables pour les méthodes et données statiques.

 

 

4.     EXERCICE D'APPLICATION

 

Considérons les déclarations :

 

CLASSE A

     privé

       x, y : entier

     public :

       PROCEDURE initial (données : i, j : entier)

       PROCEDURE affiche ()

       PROCEDURE affiche_x ()

FCLASSE

 

 

CLASSE B : hérite de A

     privé

       x, z : entier                                /* redéfinition de x */

     public

       PROCEDURE initial (données : i, j, k, l : entier) /* surcharge */

       PROCEDURE affiche ()                          /* redéfinition */

FCLASSE

 

La classe B possède ici quatre membres données de type entier. Les membres x et y sont hérités de la classe A, le membre z est ajouté, le membre x est redéfini. Un membre "donnée" redéfini ne remplace pas le membre correspondant de la classe de base, il est ajouté. Chaque objet de la classe B possédera donc les membres données x, y, z et x de A.

 

La classe B a accès aux cinq méthodes membres : initial(données : i, j, k, l : entier), initial(données : i, j : entier), affiche(), A::affiche() et affiche_x().

 

Exercice d'application :

Soient les variables unA et unB respectivement de classe A et B déclarés dans le programme principal. Remplir le tableau suivant :


 

Instruction ou

partie d'instruction

Localisation

Membre utilisé ou erreur

unB.initial (2, 3, 5, 6);

programme principal (pp)

 

initial (1, 2) ;

classe B

 

unB.initial (1, 2) ;

pp

 

x

classe B

 

A::x

classe B

 

x

classe A

 

x

pp

 

affiche_x () ;

classe B

 

A::affiche_x () ;

classe B

 

unB.affiche_x () ;

pp

 

affiche () ;

classe A

 

affiche () ;

classe B

 

A::affiche () ;

classe B

 

unB.affiche () ;

pp

 

B::affiche () ;

classe A

 

z

classe A

 

 

 

5.     DERIVATIONS EN CASCADE

 

Une classe dérivée peut à son tour être spécialisée et devenir la classe de base d’une autre classe. Par exemple la classe «  Deuxroues » peut hériter de « Véhicule » et les classes « Velo » et « Moto » peuvent hériter de « Deuxroues ». Dans ce cas, « Velo » et « Moto » héritent des membres de « Deuxroues » et implicitement des membres de « Véhicule » via « Deuxroues ».

 

Toutes les classes dérivées peuvent utiliser directement les méthodes publiques (ainsi que les données publiques) des classes ancêtres (ancêtre direct ou non).

Les principes présentés précédemment s’appliquent.


 

6.     CODAGE EN JAVA

 

6.1. Dérivation et spécialisation

 

 

q       Ecriture de la classe Véhicule

 

class Véhicule

{

          private int noveh;

          private String couleur;

          private float poids;

                             

          public void initialiser(int n, String c, float p) 

          {

                 noveh = n;

                 couleur=c;

                 poids=p;

          }     

         

          public float donnerpoids()

          {

                 return poids;

          }

 

          public String donnercouleur()

          {

                 return couleur;

          }

}

 

 

q       Ecriture de la classe Voiture

 

 class Voiture extends Véhicule

 {

        private int nbpassagers;

        private boolean métallisé;

       

        public void initialiser(int n, String c, float p, int gens, boolean

        métal) 

        {

             initialiser(n, c, p);

             nbpassagers=gens;

             métallisé=métal;

        }   

       

        public int donnerpassagers()

        {

             return nbpassagers;

        }

 

        public String donnercouleur()

        {

             return super.donnercouleur() + (métallisé ?" métallisé":" mat");

        }

 }

 

Les possibilités d'accès sont identiques à ce qui a été vu en algorithmique.


 

q       Ecriture de la classe Test2 (pour tester Voiture et Véhicule)

 

class Test2

{

 

     public static void main(String [] argv)

     {

        Véhicule véh1 = new Véhicule(), véh2 = new Véhicule();

        Voiture voit1 = new Voiture(), voit2 = new Voiture();

        float pds;

        String couleur;

        int nbpas;

            

        voit1.initialiser (1, "bleu", 1000);   // Appel à initialiser de Véhicule

        voit2.initialiser (2,"rouge",1500,7,true); // Appel à initialiser de Voiture

        véh1.initialiser (1, "vert", 800);     // Appel à initialiser de Véhicule

        véh2.initialiser (2, "jaune", 900, 7, true);  // Erreur

 

        pds = voit1.donnerpoids();             // Appel à donnerpoids de Véhicule

        pds = voit2.donnerpoids();             // Appel à donnerpoids de Véhicule

        pds = véh1.donnerpoids();       // Appel à donnerpoids de Véhicule

 

        couleur = voit1.donnercouleur();       // Appel à donnercouleur de Voiture

        couleur = voit2.donnercouleur();       // Appel à donnercouleur de Voiture

        couleur = véh1.donnercouleur();        // Appel à donnercouleur de Véhicule

                                                                                                             

        nbpas = voit1.donnerpassagers();       // Appel à donnerpassagers

                                               // Place 0 dans nbpas

 

        nbpas = voit2.donnerpassagers();       // Appel à donnerpassagers

                                               // Place 7 dans nbpas

        nbpas = véh1.donnerpassagers();        // Erreur

                                       

     }

}

 

 

 

6.2. Le modificateur final

 

Nous connaissons déjà les modificateurs de visibilité (c'est à dire les statuts public et private), nous allons découvrir final (final peut s'employer avec un autre modificateur tel private ou public).

 

Classes finales

 

Cela signifie que la classe ne pourra pas être héritée. Il ne sera alors pas possible de la dériver.

Exemple :

final class Velo

{

….

}

Toute tentative donnera un message d'erreur à la compilation.


 

Variables finales

 

Ceci est utilisé pour déclarer des constantes. Généralement, il s'agira d'une donnée statique car on ne voit pas l'intérêt d'avoir une donnée de même valeur dans chaque objet.

Exemples :

public static final double EURO = 6.5569;

 

private static final double TVA = 19.6;

On pourra en plus définir une fonction d'accès pour TVA comme on le fait pour une autre donnée privée.

public static double tva()

{

return TVA;

}

 

Méthodes finales

 

C'est une méthode qui ne peut être redéfinie. En revanche, elle pourra être surchargée.

 

 

6.3. Constructeurs

 

Il est impossible de construire un objet d'une classe dérivée avec le constructeur de la classe de base. Par exemple :

Voiture v=new Véhicule();                // Provoque une erreur à la compilation

Ce qui s'explique car le constructeur de Véhicule ne peut en aucun cas initialiser les données spécialisées de Voiture.

 

q       Ecriture de la classe Véhicule2

 

class Véhicule2

{

        private int noveh;

        String couleur;

        private float poids;

                          

        public Véhicule2 (int n, String c, float p) 

        {

             noveh=n;

             couleur=c;

             poids=p;

        }   

       

        public float donnerpoids()

        {

             return poids;

        }

 

        public String donnercouleur()

        {

             return couleur;

        }

}

 


 

q       Ecriture de la classe Voiture2

 

class Voiture2 extends Véhicule2

 {

        private int nbpassagers;

        private boolean métallisé;

 

        public Voiture2(int n, String c, float p, int gens, boolean métal) 

        {

             super(n, c, p);

             nbpassagers=gens;

             métallisé = métal;

        }   

       

        public int donnerpassagers()

        {

             return nbpassagers;

        }

 

        public String donnercouleur()

        {

             return super.donnercouleur() + (métallisé ?" métallisé":" mat");

        }

}

 

q       Ecriture de la classe Test22 (pour tester Voiture2 et Véhicule2)

 

class Test22

{

 

     public static void main(String [] argv)

     {

        Véhicule2 véh1 = new Véhicule2(1, "vert", 800);

        Véhicule2 véh2 = new Véhicule2(2,"jaune",900,7,true); // véh2 ne sera pas                                                      // créé, il y aura erreur

        Voiture2 voit1 = new Voiture2(1, "bleu", 1000, 0, false),

                 voit2 = new Voiture2(2, "rouge", 1500, 7, true);

        float pds;

        int nbpas;

        String couleur;         

 

        pds = voit1.donnerpoids();             // Appel à donnerpoids de Véhicule

        pds = voit2.donnerpoids();             // Appel à donnerpoids de Véhicule

        pds = véh1.donnerpoids();       // Appel à donnerpoids de Véhicule

 

        couleur = voit1.donnercouleur();       // Appel à donnercouleur de Voiture

        couleur = voit2.donnercouleur();       // Appel à donnercouleur de Voiture

        couleur = véh1.donnercouleur();        // Appel à donnercouleur de Véhicule

                                                                                                             

        nbpas = voit1.donnerpassagers();       // Appel à donnerpassagers

                                               // Place 0 dans nbpas

 

        nbpas = voit2.donnerpassagers();       // Appel à donnerpassagers

                                               // Place 7 dans nbpas

 

        nbpas = véh1.donnerpassagers();        // Erreur

                                       

     }

}

 

 


 

Remarques :

 

. A partir du moment où la classe de base ne possède pas de constructeur par défaut, il est impératif de prévoir un constructeur pour la classe dérivée ne serait-ce que pour transmettre

les paramètres nécessaires au constructeur de la classe de base.

Si la classe de base possède un constructeur par défaut (implicite c'est à dire aucun constructeur défini ou bien explicite), la classe dérivée n'est pas obligée de mettre en place un constructeur.

 

. Si on avait souhaité qu'une voiture puisse être construite avec ou sans paramètres, il aurait fallu prévoir en plus un constructeur sans paramètres pour Voiture2 et pour Véhicule2.

 

public Véhicule2 () 

{

              Code éventuel

}

 

public Voiture2 () 

{

              Code éventuel

}

 

 

Exercice d'application sur les constructeurs

 

Soient les classes A et B :

class A

{

     private int x, y;

    

     public  A() {}                     // constructeur par défaut

   

     public  A (int i, int j)           // constructeur paramétré

    {

             x = i ;

             y = j;

     }

}

 

class B extends A

{

     private int x, z;

    

     public  B (){}             

    

     public  B (int i, int j, int k, int l)

     {

             super(i,j) ;        //appel du constructeur de A avec ses paramètres

             x = k;

             z = l;

     }

}

 

Donner l'objet obtenu (ou erreur) pour chaque instruction de création d’objet :

Instruction

Objet créé ou erreur

B unb=new B() ;

 

B unb=new B(2,3,4,5) ;

 

B  unb =new B(1,2);

 


 

6.4. Dérivation de classes du JDK

 

Il est tout à fait possible de dériver les classes du JDK.

 

Exemple : création d'une classe Férié dérivée de la classe GregorianCalendar

 

q       Ecriture de la classe Férié

 

import java.util.GregorianCalendar;

 

class Férié extends GregorianCalendar

{

        private String nom;

        public Férié(String n, int j, int m, int a)

        {

             super(a, m-1, j);

             nom=n;

        }

       

        public String rendnom()

        {

             return nom;

        }

}

 

 

q       Ecriture de la classe Progférié qui utilise la classe Férié

 

class Progférié

{

     public static void main(String [] argv)

     {

        Férié jourférié = new Férié("Noel",25, 12, 2001);

        int jour = jourférié.get(Férié.DAY_OF_MONTH);

        int mois = jourférié.get(Férié.MONTH)+1;

        int an = jourférié.get(Férié.YEAR);

        int quant = jourférié.get(Férié.DAY_OF_YEAR);

        System.out.println (jourférié.rendnom() + " "+jour + " " + mois + " " + an);

        System.out.println ("Ce sera le " + quant + "eme jour de l'annee");

     }

}

 

 


 

 

6.5. Compléments

 

6.5.1. Classes et méthodes abstraites

 

Une classe qui n’est utilisée que pour être dérivée peut être définie comme abstraite. Toute instanciation est alors impossible.

 

Exemple : soit la hiérarchie suivante :

 

image

Zone de Texte: Oiseau
image

 

 

 

·                     Classe Animal :

 

abstract class Animal

{

String prénom;

public Animal(String p)

{

       prénom = p;

}

public String rendprénom()

{

       return prénom;

}

abstract public void crier();

}

 

Toutes les méthodes déclarées abstraites dans la classe abstraite devront être redéfinies (donc { bloc }  obligatoire avec bloc éventuellement vide) dans chaque classe dérivée que l'on veut instancier. Si l'une des méthodes abstraites n'est pas redéfinie dans une classe dérivée, cette classe dérivée est elle-même abstraite et doit être déclarée comme telle.

 

La déclaration d'une méthode abstraite dans une classe rend la classe abstraite et nous oblige à la déclarer comme telle.

 

Toutes les méthodes non abstraites d'une classe abstraite ne sont pas obligatoirement redéfinies dans les classes dérivées mais peuvent l'être.

 


 

·                     Classe Ovipare :

 

abstract class Ovipare extends Animal

{

public Ovipare(String p)

{

       super(p);

}

abstract public void crier();

}

 

Un constructeur ne peut pas être abstrait.

 

Dans cet exemple, on est obligé de définir le constructeur dans les classes dérivées car la classe de base (Animal) ne comprend pas de constructeur par défaut. Mais si on avait

défini :

public Animal(String p)

{

       prénom = p;

}

public Animal()

{

}

 

Alors, il n'y aurait pas obligation de redéfinir le constructeur.

 

 

·                     Classe Oiseau :

 

class Oiseau extends Ovipare

{

public Oiseau(String p)

{

       super(p);

}

 

public void crier()

{

             System.out.println ("Cuicui");

       }

}

 

Ainsi que nous l'avons dit ci-avant, il est obligatoire de redéfinir (donc {bloc} obligatoire avec bloc éventuellement vide) la méthode crier dans les classes que l'on veut instancier.

 


 

·                     Classe Mammifère :

abstract class Mammifère extends Animal

{

String race;

public Mammifère(String p, String r)

{

       super(p);

       race = r;

}

 

public String rendrace()

{

       return race;

}

abstract public void crier();

}

 

·                     Classe Chien :

class Chien extends Mammifère

{

public Chien(String p, String r)

{

       super(p, r);

}

 

public void crier()

{

             System.out.println ("Ouah-Ouah");

       }

}

 

·                     Classe Chat :

class Chat extends Mammifère

{

public Chat(String p, String r)

{

       super(p, r);

}

 

public void crier()

{

             System.out.println ("Miaou-Miaou");

       }

}

 

·                     Classe pour les tests :

class TestAnimaux

{     

       public static void main(String [] argv)

       {

             Chien milou = new Chien("Milou", "Berger des Pyrénées");

             Chat tigri = new Chat("Tigri", "Angora");

             Oiseau nestor = new Oiseau("Nestor");

             milou.crier();

             tigri.crier();

             nestor.crier();

       }                                

}


 

Remarques :

·           Ovipare et Mammifère sont obligées d'être abstraites ou bien de redéfinir (donc {bloc}  obligatoire avec bloc éventuellement vide) la méthode crier.

De toute façon :

-              On considère que ces 2 classes ne peuvent être instanciées donc on les déclare abstraites.

-              On considère que le cri d'un mammifère ou d'un ovipare est une notion abstraite donc on ne peut donner un corps à la méthode crier dans Ovipare et dans Mammifère donc on déclare la méthode crier comme abstraite dans ces 2 classes.

·           Une classe peut être déclarée abstraite même si elle ne contient aucune méthode abstraite.

 

6.5.2. Le polymorphisme

 

a.      Principe

Le fait qu'une référence du type de la classe parente désigne un objet d'une classe dérivée s'appelle le surcasting.

 

Ex :

Animal monanimal = new Chien("Rantanplan", "Setter");

 

ou en version longue :

 

Animal monanimal;

monanimal = new Chien("Rantanplan", "Setter");

Remarque : il est impossible de faire l'opération inverse ainsi que nous l'avons déjà vu, par exemple il est impossible d'écrire :

Chien monanimal = new Animal("Rantanplan", "Setter");

 

Le typage (ou liaison) dynamique (ou tardive) consiste à déterminer la méthode à appeler seulement lors de l’exécution en prenant en compte le type effectif de l’objet désigné par le handle et non le type déclaré du handle.

 

Java fonctionne par typage tardif sauf pour les méthodes déclarées avec le modificateur final.

Nous allons voir un exemple de typage tardif avec la nouvelle version de la classe de test.

 

·                     Nouvelle version de la classe de test :

class TestAnimaux2

{     

       public static void main(String [] argv)

       {

             Animal milou = new Chien("Milou", "Berger des Pyrénées");

             Animal tigri = new Chat("Tigri", "Angora");

             Animal nestor = new Oiseau("Nestor");

             milou.crier();

             tigri.crier();

             nestor.crier();

       }                                

}


 

Ce sont bien les méthodes crier des classes dérivées qui sont appelées au moment de l'exécution. Ceci n'a rien à voir avec le fait que la méthode crier soit abstract.

 

Attention, il faut que la méthode existe dans la classe Animal (même si ce n'est pas celle qui sera exécutée) car sinon il y a une erreur à la compilation.

 

Le polymorphisme se définit par le fait que les instances des différentes classes répondent à la même méthode mais d'une façon appropriée à chaque classe. Une même instruction provoquera l'appel de telle ou telle méthode en fonction de la classe de l'objet effectivement désigné par le handle au moment de l'exécution.

Le polymorphisme est possible grâce au système de liaison tardive.

 

 

       b. Passage de paramètres et polymorphisme

On va voir qu'une référence transmise explicitement en paramètre est également polymorphe.

 

·                     Nouvelle version de la classe Animal :

abstract class Animal

{

String prénom;

public Animal(String p)

{

             prénom = p;

}

 

public String rendprénom()

{

             return prénom;

}

abstract public void crier();

 

public void copain(Animal a)

{

             System.out.println(rendprénom() + " et " +

                            a.rendprénom() + " sont copains");

             System.out.println("Voici les cris que l'on entend : ");

             crier();

             a.crier();

}

}

 

 

·                     Nouvelle version de la classe de test :

class TestAnimaux3

{     

       public static void main(String [] argv)

       {

             Animal milou = new Chien("Milou", "Berger des Pyrénées");

             Animal tigri = new Chat("Tigri", "Angora");

             Animal nestor = new Oiseau("Nestor");

             milou.copain(tigri);       // Provoque l'affichage d'un msg disant que

                                        // Milou et Tigri sont copains puis les cris

                                        // de Milou et de Tigri

             milou.crier();

             tigri.crier();

             nestor.crier();

       }                                

}

On constate que le polymorphisme s'applique pour les 2 appels à crier qui figurent dans la méthode copain.