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
*/
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
:
·
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.