← (précédent) C.4 Exercices corrigés de P.O.O (1)
C.6 Exercices de P.O.O (2)
Accueil S O M M A I R E

C.5 Héritage et Polymorphisme

Partie 1 : Héritage

Exemple Zéro :  

1.     public class Animal1
2.     {
3.          public void marcher1()
4.          {
5.               System.out.println("marcher à 4 pattes);
6.          }
7.     }

Exemple ZéroBis :  

1.     public class Animal2
2.     {
3.          public void marcher2()
4.          {
5.               System.out.println("marcher à 1000 pattes);
6.          }
7.     }

Exemple ZéroTer :  

1.     public class TesterAnimaux
2.     {
3.          public static void main(String [] args)
4.          {
5.               Animal1 a1 = new Animal1();
6.               a1.marcher1();
7.               Animal2 a2 = new Animal2();
8.               a2.marcher2();
9.          }
10.    }

Enregistrez les classes Animal1, Animal2 et TesterAnimaux dans le même répertoire.

Compilez, puis exécuter le programme TesterAnimaux, il sera affiché :

marcher à 4 pattes
marcher à 100 pattes


Normal. Dans le programme, on a créé un objet Animal1 (a1) et on lui a appliqué la méthode marcher1() qui se trouve dans sa classe. D' où à l' affichage, on a : marche à 4 pattes

Dans le programme, on a créé un objet Animal2 (a2) et on lui a appliqué la méthode marcher2() qui se trouve dans sa classe. D' où à l' affichage, on a : marche à 1000 pattes

Maintenant, nous allons essayer d' appliquer à un objet, une méthode qui se trouve dans une classe autre que la sieene.
Exemple Zéroquater :  

1.     public class TesterAnimaux2
2.     {
3.          public static void main(String [] args)
4.          {
5.               Animal2 a2 = new Animal2();
6.               a2.marcher1();
7.               
8.          }
9.     }

Enregistrez ce programme dans le même répertoire que les autres classes. Puis compilez.
Après compilation, et il sera affiché :

TesterAnimaux2.java:6: cannot find symbol
symbol : method marcher1()
location: class Animal2
a2.marcher1();
^
1 error


Ce qui veut dire que dans la classe TesterAnimaux2.java, et en ligne 6, le compilateur ne comprend pas une syntaxe. C' est la méthode marcher1(). On ne trouve pas cette méthode dans la classe Animal2. En effet, on applique à l' objet a2 (de la classe Animal2), une méthode qui ne se trouve pas dans cette classe.

Conclusion : on applique pas à un objet, une méthode qui ne se trouve pas dans la classe à partir de laquelle l' objet a été créé.

Il existe des exceptions : le cas de l' héritage et du polymorphisme. C' est ce que nous allons voir ci-dessous.
L'héritage est un mécanisme qui permet à une classe de disposer des champs et des méthodes d �une autre classe.

Imaginez un compte bancaire. Cela n' est un secret pour personne que tous les comptes bancaires possèdent des points communs. Champs et méthodes. Mais les comptes bancaires, il y en a de toutes sortes. Comptes courants et comptes d' épargne. Pour les épargnes, on livret A, codevi, PEP, PER, etc... Pour les comptes courants, on a des comptes chèques classiques. Il y en a pour des particuliers, pour des entreprises, des personnes morales. Et je ne vous parle même pas de comptes spécifiques à des établissements bancaires précis. Certains comptes sont dépourvus de tout chèque, d' autres en ont. Des cartes bancaires, de type visa, de type retrait simple, des américan express, et j' en passe et des meillers.

VOus imaginez un peu tout ça dans une même classe ? Pourquoi pas. Mais les choses seraient moins touffus si l' on crée plusieurs classes.

D'abord une classe ayant les champs et les méthodes communes à toutes les classes qu' on appellera CompteBancaire.
Ensuite, on pourra créer 2 classes : CompteBancaireCourant ayant des champs et méthodes spécifiques à tous les comptes courants.
Puis CompteBancaireEpargne ayant des champs et méthodes spécifiques à tous les comptes d' épargne.

Ensuite, on crée une classe livretA champs et méthodes spécifiques à tous les comptes de type Livret A, une classe Codevi avec des champs et méthodes spécifiques à tous les comptes de type Codevi, etc...
Le fait de créer ces diverses classes permettent de faciliter le travail au programmeur. On le verra par des exemples.

Les classes compteBancaireCourant et CompteBancaireEpargne seront les classes dérivées de la classe CompteBancaire. La classe CompteBancaire sera appelée La classe mère des classes CompteBancaireCourant et CompteBancaireEpargne.
Ainsi, tous les champs et méthodes de type public de la classe mère pourront être appliquées aux objets des classes CompteBancaireCourant et CompteBancaireEpargne. Les champs et méthodes de type private ne sont valables que dans la classes où ils ont été définis.
Les classes LivretA et Codevi seront les classes dérivées de la classe CompteBancaireEpargne. Elles seront aussi les classes dérivées de la classes CompteBancaire. Pour cette raison, elle bénéficieront elles aussi des champs et méthodes de la classes CompteBancaire.

Le mot clé permettant de définir l' héritage est : extends . Voyons ce que ça donne dans des exemples.
Exemple 1 :  

1.    public class CompteBancaire
2.    {
3.        public String nom, prenom, adresse, tel;
4.       
5.        
6.        public CompteBancaire()
7.        {
8.            System.out.println(" Vous allez créer un compte :");
9.            System.out.print("Entrez le nom du titulaire : " );
10.           this.nom = Lire.chaine();
11.           System.out.print("\n Entrez le prénom du titulaire : " );
12.           this.prenom = Lire.chaine();
13.           System.out.print("\n Entrez l'adresse du titulaire : " );
14.           this.adresse = Lire.chaine();
15.      }
16.      
17.      public afficherAdresse()
18.      {
19.            System.out.println("adresse du compte de " + this.nom + " = " + this.adresse);
20.      }
21.  }
Voici une classe compteBancaire. Tout ce qu' il y a de simple. Je ne me suis pas cassé la tête à modéliser un vrai compte bancaire.
Exemple 1Bis :  

1.    public class CompteEpargne extends CompteBancaire
2.    {
3.                     
4.    }
Voici une classe compteEpargne très simplifiée aussi. Le mot extends signifie que la classe compteEpargne hérite des champs et méthodes de la classe CompteBancaire. Je n' ai rien mis dans cette classe pour l' instant. Et ce n' est pas une erreur.
Exemple 1Ter

1.    public class TesterHeritage1
2.    {
3.        public static void main (String [] args)
4.        {
5.            CompteEpargne ce = new CompteEpargne();
6.            ce.afficherAdresse();
7.        }
8.    }

Bien entendu, il faut enregistrer les 3 classes : CompteBancaire, CompteEpargne et TesterHeritage1 dans le même répertoire. Sans oublier la classe Lire du chapitre A.3 Lire une information au clavier . Cette classe qui permet de saisir des informations au clavier

Compilez puis exécutez ce programme. Il vous sera demandé le nom du titualire du compte que vous allez créer. Entre un nom. Puis validez par la touche "Entrée". Puis le prénom. Puis l' adresse.

Ensuite, il vous sera affiché : adresse du compte de XXX = YYY

XXX représente le nom que vous avez saisi au clavier. Et YYY représente l' adresse que vous avez saisi au clavier.

Sur la ligne 6 du programme, vous voyez c1.afficherAdresse(); Vous avez créé un objet CompteEpargne. Et vous avez pu appliquer la méthode afficherAdresse() de la classe CompteBancaire. Autrement dit, vous avez appliqué une méthode d' une classe à un objet n' appartenant pas à cette classe. Pourquoi, parce que grâce au mot clé extends, la classe CompteEpargne peut utiliser les méthodes et les les champs de la classe CompteBancaire. EN écrivant ceci : CompteEpargne cL = new CompteEpargne(); ligne 5 du programme, on appelle le constructeur de la classe CompteEpargne comme vous le savez. Or, cette classe n' a pas de constructeur. Que fait le compilateur dans ce cas ? ... il va chercher le contructeur de la classe mère. Et la classe CompteBancaire est justement la classe mère de la classe CompteEpargne.

On dit que la classe CompteEpargne est une classe dérivée de la classe CompteBancaire.
On dit aussi que la classe CompteEpargne est une classe fille de la classe CompteBancaire.
On dit aussi que la classe CompteEpargne est une classe sous-classe de la classe CompteBancaire.

On dit que la classe CompteBancaire est la super classe de la classe CompteEpargne
On dit aussi que la classe CompteBancaire est la classe mère de la classe CompteEpargne.
Bien entendu, il n' y a aucun intérêt à créer une classe dérivée vide comme ci-dessus. Une classe dérivée doit avoir ses propres champs et méthodes. C' est ce que nous voyons dans l' exemple 2.
Exemple 2 :  

1.    public class CompteEpargne2 extends CompteBancaire
2.    {
3.        public final double soldeMinimal = 1;
4.        public double tauxInteret = 3/100;
5.        
6.        public double montantApresInteret(double montantInitial)
7.        {
8.            return montantInitial * (1 + tauxInteret);
9.        }
4.    }
Ici, j' ai ajouté des éléments propres à un compte d' épargne. Dans un compte d' épargne, le solde n' est jamais négatif. Et même, il est forcément supérieur à zéro. J' ai choisi de lui donner la valeur de 1 comme ça se passe d' habitude.
Un compte d' épargne rapporte de l' argent variable selon un taux qui dépend du type de compte d' épargne. Ici, j' ai choisi de donner une valeur de 3 pour 100 ( 3/100 = 0.03).
Ensuite, j' ai doté la classe d' une méthode qui retourne le montant final après avoir appliqué le taux d' intérêt.
Exemple 2Bis :


1.    public class TesterHeritage2
2.    {
3.        public static void main (String [] args)
4.        {
5.            CompteEpargne2 cL2 = new CompteEpargne2();
6.            double sommesVersées = 2524;
7.            System.out.println("solde après un an : " + cL2.montantApresInteret(sommesVersées) );
15.       }
16.   }
Bien entendu, il faut enregistrer les 3 classes : CompteBancaire, CompteEpargne2 et TesterHeritage2 dans le même répertoire. Sans oublier la classe Lire du chapitre A.3 Lire une information au clavier . Cette classe qui permet de saisir des informations au clavier

Après compilation, puis exécution, Il vous sera demandé le nom du titualire du compte que vous allez créer. Entre un nom. Puis validez par la touche "Entrée". Puis le prénom. Puis l' adresse.

Puis il sera affiché : solde après un an : 2 599.72

Ligne 5 : on appelle le constructeur pour créer l' objet cL2. La classe CompteEpargne2 n' ayant pas de constructeur, le compilateur utilise le contructeur de la classe supérieure. La classe COmpteBancaire.

Ligne 6 : On déclare et on affecte à la variable sommesVersees une valeur. Puis en ligne 7, on demande à afficher le solde après intérêt. Ce montant est claculé grâce à la méthode montantApresInteret() de la classe CompteEpargne2.
Exemple 3 :  manipuler un champ public de la classe supérieure dans la classe dérivée.

1.    public class CompteEpargne3 extends CompteBancaire
2.    {
3.        
4.        public void modifAdresse()
5.        {
6.           System.out.print("Saisir la nouvelle adresse = ");
7.           this.adresse = Lire.chaine();
8.        }   
9.               
10.    }
Ligne 6 et 7 : on demande à l' utilisateur de saisir une nouvelle adresse. Cela permet de modifier le champ adresse de l' objet CompteLivret sur lequel on applique la méthode.
En compilant cette classe, aucun message d' erreur. Parce qu' il est possible de modifier le champs d' une classe dans l' une de ses classes dérivées. A condition que ce champ soit public.
Voyons cela dans un programme.
Exemple 3Bis :


1.    public class TesterHeritage3
2.    {
3.        public static void main (String [] args)
4.        {
5.            CompteEpargne3 cL3 = new CompteEpargne3();
6.            cL3.afficherAdresse();
7.            cL3.modifAdresse();
8.            System.out.println("La nouvelle adresse est : ");
9.            cL3.afficherAdresse();
10.       }
11.   }
Compilez puis exécutez ce programme. Lignes 5 et 6 : C'est comme d' habitude. Demande des nom, prénom et adresse. Puis affichage des valeurs de ces champs.

La ligne 7 : on appelle la méthode modifAdresse() de la classe CompteEpargne3. L' exécution de cette instruction permet d' afficher Saisir la nouvelle adresse : Puis le curseur clignote. Attendant que vous saisissiez cette adresse. Vous saisissez puis validez. Enfin, l' instruction en ligne 8 affiche La nouvelle adresse est : . Puis l' instruction de la ligne 9 appelle la méthode afficherAdresse() de la classe CompteBancaire. Sur la ligne suivante, il sera alors affiché : adresse du compte de XXX = YYY.

XXX est le nom du titulaire du compte que vous aurez saisi au début du programme. Et YYY est la deuxième adresse saisie.

Une méthode d' une classe dérivée a accès aux champs publics de sa classe de base.
Classe de base = classe mère.


Mais attention, le principe d' encapsulation disparaît alors. Pourtant, en P.O.O, il est vivement conseillé de protéger les données d' une classe en les déclarant private. Que faire alors ?
Un autre mot clé entre en jeu. C' est le mot clé protected. Voyons l' exemple ci-dessous.
Exemple 4 :  

1.    public class CompteBancaire2
2.    {
3.        private String nom, prenom, adresse, tel;
4.        protected double montantInitial;
5.        
6.        public CompteBancaire()
7.        {
8.            System.out.println(" Vous allez créer un compte :");
9.            System.out.print("Entrez le nom du titulaire : " );
10.           this.nom = Lire.chaine();
11.           System.out.print("\n Entrez le prénom du titulaire : " );
12.           this.prenom = Lire.chaine();
13.           System.out.print("\n Entrez l'adresse du titulaire : " );
14.           this.adresse = Lire.chaine();
15.      }
16.      
17.      public afficherAdresse()
18.      {
19.            System.out.println("adresse du compte de " + this.nom + " = " + this.adresse);
20.      }
21.  }
Nouvelle classe CompteBancaire avec des champs privés (private) et protégés (protected).
Exemple 4Bis :  manipuler un champ privé ou protégé de la classe supérieure dans la classe dérivée.

1.    public class CompteEpargne4 extends CompteBancaire2
2.    {
3.        
4.        public void modifAdresse()
5.        {
6.           System.out.print("Saisir la nouvelle adresse = ");
7.           this.adresse = Lire.chaine();
8.           System.out.println("Saisir nouveau montant initial");
9.           this.montantInitial = Lire.reelDouble();
8.        }   
9.               
10.    }
Enregistrez les classes CompteBancaire2 et CompteEpargne4 dans le même répertoire. Y compris la classe Lire.java

Compilez la classe CompteEpargne4. Un message d' erreur sera affiché :

CompteEpargne4.java:7: adresse has private access in CompteBancaire
this.adresse = Lire.chaine();

^
1 error

Ce qui veut dire que l' erreur se trouve en ligne 7 : l' accès à la variable adresse est privée. Donc, non accessible depuis une autre classe. Une méthode d' une classe dérivée n' a pas accès aux champs privés de sa classe de base.

Puisqu' une erreur ne s' est pas affichée concernant le champ montantInitial, on peut dire que :

Une méthode d' une classe dérivée a accès aux champs protégés (protected) de sa classe de base.
Rappel sur les constructeurs

Supposons une classe appelée Quelconque .
L' instruction Quelconque q = new Quelconque(arguments éventuels); permet de créer (d' instancier) un objet à partir de cette classe.

Cette instruction fait appel à un constructeur.
Soit la classe Quelconque ne possède pas de constructeur. Et le nombre d' arguments est égal à zéro. Dans ce cas, un constructeur par défaut est automatiquement fourni à la classe. Il permet d' initialiser les champs de l' objet à l' état zéro.
Soit la classe possède un constructeur, dans ce cas, c' est ce constructeur qui est appelé. Et dans les parenthèses, le nombre et le type des arguments correspond à celui du constructeur. Si cela ne correspond pas, un message d' erreur est affiché à la compilation.
Soit la classe possède plusieurs constructeurs, et c' est le constructeur dont le nombre et le type d' argument correspond qui est appelé. Si aucun des constructeurs ne correspond, un message d' erreurs est affiché à la compilation.

Attention ! ! ...

A partir du moment où la classe possède au moins un constructeur, le constructeur par défaut qui permet d' initialiser les champs de l' objet à l' état de zéro disparaît. Si vous voulez aussi construire un objet avec les champs à l' état de zéro, il faudra, en plus des autres constructeurs, en créer un autre pour initialiser les champs de l' objet à l' état zéro.

Lorsque nous avons affaire à une classe dérivé (crée par héritage d' une classe de base), les choses sont ( parfois ) légèrement différentes.

J' ai bien dit : Parfois, pas forcément !
Exemple 5 : Les constructeurs de la classe dérivée : partie 1 :
La classe de base et la classe dérivée possèdent chacun un constructeur

1.     public class CompteBancaire3
2.     {
3.     
4.          private String nom, prenom, adresse, tel;
5.          private double montantInitial;
6.          public CompteBancaire3(String nom, String prenom, String adresse, String tel)
7.          {
8.              this.nom = nom; this.prenom = prenom; this.adresse = adresse;
9.              this.montantInitial = montantInitial;
10.         }
11.         
12.    }
Dans cette classe, un constructeur. Dans une classe dérivée, y a en général d' autres champs et d' autres méthodes. Sinon, la classe dérivée ne sert à rien. Mais sachez que dans une classe dérivée, il est possible qu' il n' y ait pas d' autres champs. Mais plutôt d' autres méthodes. Dans ce cas, un constructeur dans la classe dérivée ne sert à rien. Puis que la création d' un objet de cette classe fera automatiquement appel au constructeur de la classe de base. Il faut donc au moins un champ dans la calsse dérivée.
Remarquez, il est possible que dans la classe dérivée, il n 'y ait pas de champ et qu' un constructeur soit quand même nécessaire. Pourquoi ? ... parce que on veut peut-être que les objets de la classe dérivée soient initialisés de façon différente des objets de la classe de base. Même dans ce cas, il est encore inutile de même un constructeur dans la classe dérivée. Car il suffit pour cela de mettre un deuxième constructeur dans la classe de base. Et c' est ce constructeur qu' on appellera pour initialiser les objets de la classe dérivé.
En fin de compte, il faut absolument au moins un champ propre à la classe dérivée.
Exemple 5Bis : Les constructeurs de la classe dérivée : partie 1 :
La classe de base et la classe dérivée possèdent chacun un constructeur

1.     public class CompteEpargne5 extends CompteBancaire3
2.     {
3.          private double soldeMinimal;
4.          
5.          public CompteEpargne4(double soldeMinimal)
6.          {
7.               this.soldeMinimal = soldeMinimal;
8.          }                   
9.    }
La classe CompteEpargne5 est dérivée de la classe CompteBancaire3. Elle a son propre constructeur qui permet d' initialiser son seul champ : soldeMinimal.
Exemple 5ter : Les constructeurs de la classe dérivée : partie 1 :

1.     public class TesterConstructeursDerives
2.     {
3.          public static void main(String [] args)
4.          {
5.               CompteEpargne4 ce4 = new CompteEpargne4(250.25);
6.               
7.          }                   
8.     }
Les trois classes : CompteBancaire3, CompteEpargne5 et TesterConstructeursDerives doivent être enregistrés dans le même répertoire.
Compilez ce programme et il sera affiché un message d' erreur :

TesterConstructeursDerives.java:5: cannot find symbol
symbol : constructor CompteBancaire3()
location: class CompteBancaire3
{
^
1 error

Ce qui veut dire que dans la classe TesterConstructeursDerives.java, et en ligne 5, on a une erreur. Le compilateur n' arrive pas à trouver le constructeur CompteBancaire3(). Ce constructeur se trouve en classe CompteBancaire3.
Voilà une erreur un peu étonnante. La ligne 5 de la classe TesterConstructeursDerives permet de créer un objet de type CompteEpargne5. Donc, c' est le constructeur de la classe CompteEpargne5 qui doit être utilisé. Pourquoi le compilateur nous parle du constructeur de la classe CompteBancaire3 (classe lère) ? ... La réponse à cette question est la suivante:
Le constructeur d' une classe dérivée doit initialiser tous les champs de la classe dérivé (logique). Mais aussi ceux de la classe de base. En effet, l' objet créé à partir de la classe dérivé dispose des champs de la classe dérivé et ceux de la classe de base. Même si les champs de la classe de base sont déclarés en private. le mot private signifie seulement que les champs de la classe de base ne peuvent pas être manipulés dans la classe dérivé. Mais ces champs sont quand même disponibles pour l' objet dérivé.
C' est pourquoi dans le constructeur de la classe dérivé, il faut aussi initialiser les champs de la classe mère. Si ces champs sont privés, on ne peut pas les manipuler à partir du constructeur dérivé. Mais on peut faire appel au constructeur de la classe mère pour les initialiser. Pour cela, on utilise le mot clé : super .
Exemple 5quater : Les constructeurs de la classe dérivée : partie 1 :
La classe de base et la classe dérivée possèdent chacun un constructeur

1.     public class CompteEpargne6 extends CompteBancaire3
2.     {
3.          private double soldeMinimal;
4.          
5.          public CompteEpargne6(String nom, String prenom, String adresse, String tel, double soldeMinimal)
6.          {
7.               super(nom, prenom, adresse, tel);
8.               this.soldeMinimal = soldeMinimal;
9.          }                   
10.    }
La classe CompteEpargne6 hérite des champs et méthodes de la classe CompteBancaire3. Donc le constructeur de la classe CompteEpargne6 doit initialiser ses champs et celui de la classe CompteBancaire3. D' où les arguments String nom, String prenom, String adresse et String tel pour initialiser les champs de la classe CompteBancaire et l' argument double soldeMinimal pour initialiser le champ de la classe CompteEpargne6.
En ligne 8 : j' initialise le champ de CompteEpargne6. Et en ligne 7 : j' utilise le constructeur de la classe CompteBancaire. Le mot clé super fait référence au constructeur de la classe de base. Et les 4 arguments correpondent aux 4 arguments du constructeur de la classe CompteBancaire3.

Tiens ! ça ressemble à quelque chose déjà vu. Lorsqu' un constructeur d' une classe fait appel à un autre constructeur de cette même classe, on utilise le mot clé this suivi de parenthèses à l' intérieur desquelles on trouve des arguments éventuels.
Ici, c' est plutôt un constructeur de la classe dérivée qui fait appel au constructeur de la classe mère. On utilise super
Ne confondez pas ces 2 situations.

Maintenant, mettons cela dans un programme.

1.     class CompteBancaire4
2.     {
3.     
4.          private String nom, prenom, adresse, tel;
5.          
6.          public CompteBancaire4(String nom, String prenom, String adresse, String tel)
7.          {
8.              this.nom = nom; this.prenom = prenom; this.adresse = adresse;
9.              this.tel = tel;
10.         }
11.         
12          public void afficherChampsBase()
13.         {
14.               System.out.println("Nom du titulaire = " + this.nom);
15.               System.out.println("Prenom du titulaire = " + this.prenom);
16.               System.out.println("Adresse du titulaire = " + this.adresse);
17.               System.out.println("Téléphonne du titulaire = " + this.tel);
18.         }
19.    }
20.     
21.    class CompteEpargne7 extends CompteBancaire4
22.    {
23.         private double soldeMinimal;
24.          
25.         public CompteEpargne7(String nom, String prenom, String adresse, String tel, double soldeMinimal)
26.         {
27.              super(nom, prenom, adresse, tel);
28.              this.soldeMinimal = soldeMinimal;
29.         }                   
30.         
31.         public void afficheChampsDerives()
32.         {
33.              System.out.println("Solde minimal du titulaire = " + this.soldeMinimal);
34.         }
35.    }
36.    
37.    public class TesterConstructeursDerives2
38.    {
39.          public static void main(String [] args)
40.          {
41.               CompteEpargne7 ce7 = new CompteEpargne7("BELL", "Paul", "2 Rue du lac 75000 PARIS", "0606060606", 255.00);
42.               ce7.afficheChampsDerives();
43.               ce7.afficherChampsBase();
44.          }
45.    }
Rappel : Les classes CompteBancaire4, CompteEpargne7 et TesterConstructeursDerives2 sont dans le même fichier. Puisque seule la classe TesterConstructeursDerives2 est déclarée avec le mot public, le fichier doit être enregistré sous : TesterConstructeursDerives2.java

Compilez puis exécutez ce programme. Il sera affiché :
Solde minimal du titulaire = 255.0
Nom du titulaire = BELL
Prenom du titulaire = Paul
Adresse du titulaire = 2 Rue du lac 75000 PARIS
Téléphonne du titulaire = 0606060606


Explications :

Ligne 41 : on crée un objet de type CompteEpargne7 en appelant le constructeur de la classe CompteEpargne7. Normal. Ce constructeur a pour définition, 2 instructions (lignes 27 et 28). En ligne 27, on appelle le constructeur de la classe de base à l' aide du mot clé super. Ce constructeur requiert 4 arguments. D' où aussi les 4 arguments de super. Le mot super remplace le nom de ce constructeur.
En ligne 28 : on affecte à la variable soldeMinimal (de l' objet qu' on est entrain de créer), la valeur soldeMinimal passé en paramètre.

La ligne 42 : on appelle la, méthode afficheChampsDerives() de la classe dérivée. Ce qui permet d' afficher le solde minimal du titulaire du compte. Et en ligne 43 : on appelle la méthode afficherChampsBase() de la classe de base. D' où l' affichage des 4 lignes suivantes.

rappel : Sur le même objet, on applique les méthodes de deux classes. Normal. Les 2 classes sont liés par l' héritage. Donc on peut appliquer les 2 méthodes à un objet dérivé.
Rappel :

Lorsqu' un constructeur fait appel à un autre constructeur de la même classe, il y a utilisation du mot clé this suivi des parenthèses à l' intérieur desquelles on trouve un nombre de paramètres égal au nombre d' arguments du contructeur appelé. Et nous savons que cet appel est forcément la première instruction du constructeur appelant.

Lorsque un constructeur d' une classe A fait appel à un constructeur appartenant une classe B (Et dans ce cas, B est forcément la classe de base ou classe-mère de A ), il y utilisation du mot clé super suivi des parenthèses à l' intérieur desquelles on trouve un nombre de paramètres égal au nombre d' arguments du contructeur appelé. Et nous savons que cet appel est forcément la première instruction du constructeur appelant.

Exemple 6 : Les constructeurs de la classe dérivée : partie 2 :
La classe de base ne possède aucun un constructeur

1.     class CompteBancaire5
2.     {
3.     
4.          private String nom, prenom, adresse, tel;
5.          private double montantInitial;
6.          
7.     }
8. 
9.
10.     public class CompteEpargne8 extends CompteBancaire5
11.     {
12.          private double soldeMinimal;
13.          
14.          public CompteEpargne8(double soldeMinimal)
15.          {
16.               // super();
17.               this.soldeMinimal = soldeMinimal;
18.          }                   
19.    }
Enregistrez ceci dans un même fichier. Bien entendu, le fichier s' appellera CompteEpargne6.java puisque seule la classe CompteEpargne6 est déclarée avec le mot clé public.

Remarquez le double slash // en ligne 16. Ce qui veut dire que l' instruction super(); est placée en commentaires. Compilez cette classe ( javac CompteEpargne8.java ) sans l' instruction super(); (maintient du double slash //), et avec l' instruction super(); (suppression du double slash // )Dans les deux cas, aucun message d' erreur.

Explications :

D' abord, n' oubliez pas que le constructeur de la classe dérivée doit initialiser les champs de la classe dérivée, mais aussi les champs de la classe de base. Très important d' avoir ça à l' esprit.

La classe de base n' ayant pas de constructeur, le compilateur va quand même chercher un constructeur de cette classe. Rappelez-vous : si une classe n' a pas de constructeur, il y en a un par défaut qui lui est fourni. Ce constructeur ne requiert aucun argument et il initialise chaque champ à l' état zéro.
Dans le cas où la classe de base n' a pas de constructeur défini, le compilateur va donc trouver ce pseudo-constructeur. Puis l' initialisation des champs de la classe de base se fait alors. Lorsqu' on utilise l' instruction super(); le compilateur va chercher le constructeur de la classe de base qui n' a pas d' argument (comme super() ). Il n ' y en a pas de défini. Mais il y a le pseudo-constructeur par défaut. Ce dernier est sans arguments. Donc, il correspond à ce que cherche le compilateur.
L' appel super(); ne nuit donc pas. Mais il est inutile ici.
Exemple 7 : Les constructeurs de la classe dérivée : partie 3 :
La classe dérivée ne possède aucun un constructeur : plusieurs cas se présentent alors 
Dans tous les cas, l' utilisation du  mot clé super n' a plus lieu. Normal ! où le mettre ?...

premier cas : la classe de base ne possède pas de constructeur

1.     class CompteBancaire6
2.     {
3.          private double montantInitial, retraitMaximum, DecouvertMaximum;
4.          
5.          public void afficherChampsBase()
6.          {
7.               System.out.println("montant Initial du titulaire = " + this.montantInitial);
8.               System.out.println("retrait Maximum du titulaire = " + this.retraitMaximum);
9.               System.out.println("Découvert Maximum du titulaire = " + this.DecouvertMaximum);
10.              
11.         }
12.    }
13.
14.     class CompteEpargne9 extends CompteBancaire6
15.     {
16.           private double soldeMinimal;
17.           
18.           public void afficheChampsDerives()
19.           {
20.               System.out.println("soldeMinimal = " + this.soldeMinimal);
21.           }
22.    }
23.
24.    public class TesterConstructeursDerives3
25.    {
26.          public static void main(String [] args)
27.          {
28.               CompteEpargne9 ce9 = new CompteEpargne7();
29.               ce9.afficherChampsBase();
30.               ce9.afficheChampsDerives();
31.          }
32.    }
 
Enregistrez ce programme sous TesterConstructeursDerives3.java Compilez, puis exécutez ce programme, il sera affiché :

montantInitial du titulaire = 0.0
retraitMaximum du titulaire = 0.0
DÚcouvertMaximum du titulaire = 0.0
soldeMinimal = 0.0


Explications :

La classe dérivé (CompteEpargne9 ligne 14) ne possède pas de constructeur. Donc, la création d' un objet dérivé fait appel au constructeur par défaut de la classe dérivé. C' est un constructeur sans argument. Ce constructeur sans argument fait appel au constructeur de la classe de base pour initialiser les champs de la classe de base. Puisque le seul constructeur de la classe dérivé (celui par défaut) n' a pas d' argument, il fera forcément appel à un constructeur sans argument de la classe de base. Or, il n' y a pas de constructeur défini dans la classe de base. Mais il y a le constructeur par défaut de la classe de base. Ce constructeur est par définition sans argument. Donc, le contructeur sans argument de la classe dérivé trouve ce constructeur sans argument de la classe de base.
Les 2 constructeurs sans argument (base et dérivé), comme nous le savons, initialisent les champs de leurs classe à l' état zéro. C' est pourquoi à l' affichage, tous les champs (classe de base et classe dérivée), de type double, ont pour valeur 0.0
D' où l' affichage correspondant, après l' appel des méthodes d' affichage des classes de base et dérivée.

Exemple 8 : Les constructeurs de la classe dérivée : partie 3 :
La classe dérivée ne possède aucun un constructeur : plusieurs cas se présentent alors 
Dans tous les cas, l' utilisation du  mot clé super n' a plus lieu. Normal ! où le mettre ?...

deuxième cas : la classe de base possède un constructeur sans argument

1.     class CompteBancaire7
2.     {
3.          private double montantInitial, retraitMaximum, DecouvertMaximum;
4.          
5.          public CompteBancaire7()
6.          {
7.               System.out.println("montant Initial du titulaire = " + this.montantInitial);
8.               System.out.println("retrait Maximum du titulaire = " + this.retraitMaximum);
9.               System.out.println("Découver tMaximum du titulaire = " + this.DecouvertMaximum);
10.              
11.         }
12.    }
13.
14.     class CompteEpargne10 extends CompteBancaire7
15.     {
16.           private double soldeMinimal;
17.           
18.           public void afficheChampsDerives()
19.           {
20.               System.out.println("soldeMinimal = " + this.soldeMinimal);
21.           }
22.    }
23.
24.    public class TesterConstructeursDerives4
25.    {
26.          public static void main(String [] args)
27.          {
28.               CompteEpargne10 ce10 = new CompteEpargne10();
29.               
30.               ce10.afficheChampsDerives();
31.          }
32.    }
 
Enregistrez ce programme sous TesterConstructeursDerives4.java Compilez, puis exécutez ce programme, il sera affiché :

montantInitial du titulaire = 0.0
retraitMaximum du titulaire = 0.0
DécouvertMaximum du titulaire = 0.0
soldeMinimal = 0.0


Explications :

La classe dérivé (CompteEpargne10 ligne 14) ne possède pas de constructeur. Donc, la création d' un objet dérivé fait appel au constructeur par défaut de la classe dérivé. C' est un constructeur sans argument. Ce constructeur sans argument fait appel au constructeur de la classe de base pour initialiser les champs de la classe de base. Puisque le seul constructeur de la classe dérivé (celui par défaut) n' a pas d' argument, il fera forcément appel à un constructeur sans argument de la classe de base. Et justement, la classe de base possède un constructeur sans arguments (ligne 5 ). C' est donc ce constructeur qui sera appelé. J' y ai mis les mêmes intructions que la méthode afficheChampsBase(). Et dans le programme, il y a appel de la méthode afficheChampsDerives(). D' où le même résultat que dans l' exemple précédent.
Exemple 9 : Les constructeurs de la classe dérivée : partie 3 :
La classe dérivée ne possède aucun un constructeur : plusieurs cas se présentent alors 
Dans tous les cas, l' utilisation du  mot clé super n' a plus lieu. Normal ! où le mettre ?...

Troisième cas : la classe de base possède un constructeur avec arguments

1.     class CompteBancaire8
2.     {
3.          private double montantInitial, retraitMaximum, DecouvertMaximum;
4.          
5.          public CompteBancaire8(double montantInitial, double retraitMaximum, double DecouvertMaximum)
6.          {
7.               this.montantInitial = montantInitial;
8.               this.retraitMaximum = retraitMaximum;
9.               this.DecouvertMaximum = DecouvertMaximum;
10.              
11.         }
12.    }
13.
14.     class CompteEpargne11 extends CompteBancaire8
15.     {
16.           private double soldeMinimal;
17.           
18.           public void afficheChampsDerives()
19.           {
20.               System.out.println("soldeMinimal = " + this.soldeMinimal);
21.           }
22.    }
23.
24.    public class TesterConstructeursDerives5
25.    {
26.          public static void main(String [] args)
27.          {
28.               CompteEpargne11 ce11 = new CompteEpargne8();
29.               
30.               ce8.afficheChampsDerives();
31.          }
32.    }
 
Enregistrez ce programme sous TesterConstructeursDerives5.java Compilez ce programme, une erreur sera affichée :

TesterConstructeursDerives5.java:14: cannot find symbol
symbol : constructor CompteBancaire9()
location: class CompteBancaire9
class CompteEpargne11 extends CompteBancaire8
^
1 error

Ce qui veut dire que dans la classe TesterConstructeursDerives5.java, en ligne 14, c' est à dire dans la classe CompteEpargne11, le compilateur ne trouve pas de constructeur CompteBancaire8(). Autrement dit, un constructeur de la classe CompteBancaire8 sans arguments. Normal. L' instruction CompteEpargne11 ce11 = new CompteEpargne11(); en ligne 28, fait appel au constructeur de la classe CompteEpargne8. Or il n 'y en a pas de défini. Donc, c' est le constructeur par défaut (sans argument) qui est appelé. Puisqu' il faut aussi initialiser les champs de la classe de base, ce constructeur par défaut (sans argument) de la classe CompteEpargne11 va chercher un constructeur sans argument de la classe de base. Or, il y un constructeur avec arguments dans la classe de base. Donc, le constructeur par défaut (sans argument) de cette classe de base n' existe plus. D' où l' affichage de l' erreur.

Remarquez :

Si la classe de base possède plusieurs constructeurs, et que l' un d' entre eux est sans arguments, c' est celui-là qui sera appelé. Donc, pas de message d' erreur dans ce cas là.
La notion d' héritage peut se répéter à l' infini. A partir d' une classe A, on peut créer une classe dérivée B. Puis, à partir de la classe dérivée B, on peut aussi créer une classe dérivée C. Ainsi de suite.... On appelle cela, dérivations successives

Que se passe-t-il pour les constructeurs des 2ème, 3ème, ... ou nième classe dérivée ?

Par ailleurs, à partir d' une même classe de base, on peut créer une infinité des classes dérivées. On appelle cela, dérivations multiples

Attention ! Attention ! Attention ! ! ! ...

En langage java, l' héritage multiple n' existe pas.. Une classe ne peut hériter que d' une et une seule classe.

L' héritage multiple existe en langage C++
En cas de dérivations multiples, c' est à dire : plusieurs classes dérivées ayant la même classe de base.

Il n' y a pas de mystère. La construction d' un objet de chacune des classes dérivées se fait comme dans les exemples ci-dessus : les constructeurs de la classe dérivée : partie 1, partie 2 et partie 3.

Les classes dérivées d' une même classe de base n' ont aucune relation entre elles. Chacune des classes dérivées a une relation avec la classe de base.

En cas de dérivations successives, c' est à dire : plusieurs classes liées par dérivation.

public class Base

public class Derive1 extends Base

public classe Derive2 extends Derive1

public classe Derive3 extends Derive2
etc, etc...

Base est la classe mère de Derive1, la classe grand-mère de Derive2, la classe arrière grand-mère de Derive3... Cela peut paraître amusant. Mais oui, utiliser le vocabulaire familial n' est pas incongrue. Après tout, il faut bien distinguée les diverses classes en cas de dérivations successives.

De même, la classe Derive1 est la classe fille de Base, la classe Derive2 est la classe petite-fille de Base et la classe Derive3 est la classe arrière petite-fille de Base.

Que se passe-t-il pour les constructeurs dans ce cas ? ...

Là encore, je vous dirais encore : pas de mystère. Vous créez la classe de base (Base). Puis vous créez la première classe dérivée (Derive1). Vous respectez ce qui a été dit concernant les constructeurs. Puis, en créant la classe Derive2, Derive1 devient une clase de base. Sans tenir compte de la classe de base (Base), vous respectez ce qui a été dit concernant les constructeurs dérivés. Ainsi de suite jusqu' à la dernière classe dérivée.

Voyons quand même 2 exemples
Exemple 10 : Les constructeurs des classes dérivées successives.
Cas où toutes les classes ont un constructeur 

La classe de base, la classe dérivée et la classe dérivée 2ème possèdent chacune un constructeur.


1.     class CompteBancaire9
2.     {
3.     
4.          private String nom, prenom, adresse, tel;
5.          
6.          public CompteBancaire9(String nom, String prenom, String adresse, String tel)
7.          {
8.              this.nom = nom; this.prenom = prenom; this.adresse = adresse;
9.              this.tel = tel;
10.         }
11.         
12          public void afficherChampsBase()
13.         {
14.               System.out.println("Nom du titulaire = " + this.nom);
15.               System.out.println("Prenom du titulaire = " + this.prenom);
16.               System.out.println("Adresse du titulaire = " + this.adresse);
17.               System.out.println("Téléphonne du titulaire = " + this.tel);
18.         }
19.    }
20.     
21.    class CompteEpargne12 extends CompteBancaire9
22.    {
23.         private double soldeMinimal;
24.          
25.         public CompteEpargne12(String nom, String prenom, String adresse, String tel, double soldeMinimal)
26.         {
27.              super(nom, prenom, adresse, tel);
28.              this.soldeMinimal = soldeMinimal;
29.         }                   
30.         
31.         public void afficherChampsDerives()
32.         {
33.              System.out.println("Solde minimal du titulaire = " + this.soldeMinimal);
34.         }
35.    }
36.    
37.    class LivretA extends CompteEpargne12
38.    {
39.         private double plafond;
40.         
41.         public LivretA(String nom, String prenom, String adresse, String tel, double soldeMinimal, double plafond)
42.         {
43.             super(nom, prenom, adresse, tel, soldeMinimal);
44.             this.plafond = plafond;
45.         }
46.
47.         public void afficherChampsDerives2()
48.         {
49.              System.out.println("plafond du titulaire = " + this.plafond);
50.         }
51.   }
52.
53.
54.    public class TesterConstructeursDerives6
55.    {
56.          public static void main(String [] args)
57.          {
58.               LivretA LA = new LivretA("BELL", "Paul", "2 Rue du lac 75000 PARIS", "0606060606", 255.00, 115000.00);
59.               LA.afficherChampsBase();
60.               LA.afficherChampsDerives();
61.               LA.afficherChampsDerives2();
63.          }
64.    }     


Enregistrez sous TesterConstructeursDerives6.java

Compilez puis exécutez ce programme. Il sera affiché :

Nom du titulaire = BELL
Prenom du titulaire = Paul
Adresse du titulaire = 2 Rue du lac 75000 PARIS
Téléphonne du titulaire = 0606060606
Solde minimal du titulaire = 255.0
plafond du titulaire = 115000.00

Explications :

Rappel : En java, le constructeur de la classe dérivée doit initialiser ses propres champs, et aussi les champs de sa classe de base.

Le constructeur de la classe dérivée CompteEpargne12 (ligne 25) initialise son propre champ (ligne 28). Mais avant, il initialise les champs de sa classe de base en faisant appel au constructeur de celui-ci par le mot clé super (ligne 27).

Le constructeur de la classe dérivée LivretA (ligne 41) initialise son propre champ (ligne 44). Mais avant, il initialise les champs de sa classe de base en faisant appel au constructeur de celui-ci par le mot clé super (ligne 43). Puisque cette classe de base est déjà la classe dérivée de CompteBancaire9 dont elle initialise les chapmps par le mot clé super, alors forcément, la classe dérivée 2ème LivretA initialise aussi les champs de la classe CompteBancaire

GENERALISATION

Quelque soit le nombre de dérivations, la dernière dérivée doit initialiser ses propres champs, et aussi tous les champs de tous ses parents et ancêtres.

Le mot clé super fait référence à la classe de base immédiate. Donc, toujours la classe-mère. Pas grand-mère, encore moins arrière grand-mère. On dit aussi, la classe immédiatement supérieure. Certains disent aussi classe de base immédiate.
Exemple 11 : Les constructeurs des classes dérivées successives.
Cas où aucune des classes n' a un constructeur 

1.     class Base
2.     {
3.          private int a; private double b;     
4.          
5.          public void afficheChampsBase()
6.          {
7.               System.out.println("Valeur de a = " +  this.a);
8.               System.out.println("Valeur de b = " +  this.b);
9.          }
10.   }
11.
12.
13.    class Derive1 extends Base
14.    {
15.         private int a1; private double b1;
16.       
17.         public void afficheChampsDerives1()
18.         {
19.              System.out.println("Valeur de a1 = " +  this.a1);
20.              System.out.println("Valeur de b1 = " +  this.b1);
21.         }
22.    }
23. 
24.
25.    class Derive2 extends Derive1
26.    {
27.         private int a2; private double b2;
28.       
29.         public void afficheChampsDerives2()
30.         {
31.              System.out.println("Valeur de a1 = " +  this.a2);
32.              System.out.println("Valeur de b1 = " +  this.b2);
33.         }
34.    }
35.
36.
37.    public class TesterConstructeursDerives7
38.    {
39.         public static void main(String [] args)
40.         {
41.              Derive2 d2 = new Derive2();
42.              d2.afficheChampsBase();
43.              d2.afficheChampsDerives1();
44.              d2.afficheChampsDerives2();
45.         }
46.    }
Enregistrez sous TesterConstructeursDerives7.java

Compilez puis exécutez ce programme. Il sera affiché :

Valeur de a = 0
Valeur de b = 0.0
Valeur de a1 = 0
Valeur de b1 = 0.0
Valeur de a2 = 0
Valeur de b2 = 0.0

Explications :

Ligne 41 : on créé un objet de type Derive2. Cette classe n' a pas de constructeur défini. Donc, un constructeur par défaut est automatiquement fourni à la classe Derive2. Ce constructeur initialise les champs de la Derive2 à l' état zéro. Donc, a2 = 0 et b2 = 0.0
Mais comme nous le savons déjà, ce constreur fera appel au constructeur de la classe de base immédiate (Derive1) pour initialiser ses champs. Comme Derive1 n' a pas de constructeur défini, c' est donc le constructeur par défaut de Derive1 qui est appelé. Lui aussi initialise ses champs à l' état zéro. Donc, a1 = 0 et b1 = 0.0

Mais comme nous le savons aussi, ce construeur fera appel au constructeur de sa classe de base immédiate (Base) pour initialiser ses champs. Comme Base n' a pas de constructeur défini, c' est donc le constructeur par défaut de Base qui est appelé. Lui aussi initialise ses champs à l' état zéro. Donc, a0 = 0 et b = 0.0

Puis en ligne 42, on applique à cet objet, la méthode afficheChampsBase() de la classe de base. Cette méthode permet d' afficher les valeur des champs de la classe Base.
Ligne 43, on applique à cet objet, la méthode afficheChampsDerive() de la classe de base. Cette méthode permet d' afficher les valeur des champs de la classe Derive1.
Enfin en ligne 43, on applique à cet objet, la méthode afficheChampsDerive2() de la classe de base. Cette méthode permet d' afficher les valeur des champs de la classe Derive2.

D' où l' affichage.
L' exemple ci-dessus peut se concevoir autrement.
Au lieu d' écrire les lignes 42, 43 et 44, on peut se contenter d' écrire seulement la ligne 44. Mais avant cela, on appellera les méthodes afficheChampsDerives1() et afficheChampsBase() dans la méthode afficheChampsDerives2().
La méthode afficheChampsDerives2() serait donc définie ainsi :

public void afficheChampsDerives2()
{
    afficheChampsBase();
    afficheChampsDerives1();
    System.out.println("Valeur de a1 = " + this.a2);
    System.out.println("Valeur de b1 = " + this.b2);
}

Ainsi, la méthode afficheChampsDerives2() contient comme première instruction, afficheChampsBase(); pour afficher les champs de la classe de base et comme deuxième instruction, afficheChampsDerives1(); pour afficher les champs de la classe Derive1.
En fin de compte, ce sera le même affichage que l' exemple précédent.

On peut encore arriver au même affichage que l' exemple précédent en procédant ainsi.
Dans la méthode afficheChampsDerives1(), on appelle la méthode afficheChampsBase().
Puis dans la méthode afficheChampsDerives2(), on appelle la méthode afficheChampsDerives2().

public void afficheChampsDerives1()
{
    afficheChampsBase();
    System.out.println("Valeur de a1 = " + this.a1);
    System.out.println("Valeur de b1 = " + this.b1);
}

public void afficheChampsDerives2()
{
    afficheChampsDerive1();
    System.out.println("Valeur de a2 = " + this.a2);
    System.out.println("Valeur de b2 = " + this.b2);
}

En définissant les méthodes afficheChampsDerives1() et afficheChampsDerives2() comme ci-dessous, et en supprimant les lignes 42 et 43, vous arriverez au même affichage que dans l' exemple ci-dessus. Car, la méthode afficheChampsDerives1() appelle la méthode afficheChampsBase() pour afficher les champs de la classe de base. Et la méthode afficheChampsDerives2() appelle la méthode afficheChampsDerive1() pour afficher les champs de la classe de Derive1.

Mais il y a encore une autre façon d' arriver à afficher tous les champs de toutes classes en supprimant les lignes 42 et 43. Pour cela, on commence par donner le même nom aux méthodes afficheChampsBase(), afficheChampsDerive1() et afficheChampsDerive2(). Par exemple, on les appelle toutes afficheChamps()
Puis, dans la méthode afficheChamps() de la classe Derive1, on appelle la méthode afficheChamps() de la classe Base.
Puis, dans la méthode afficheChamps() de la classe Derive2, on appelle la méthode afficheChamps() de la classe Derive1.
Mais il y aura un problème. Si j' appelle la méthode afficheChamps() de la classe Base, dans la méthode afficheChamps() de la classe Derive1. Donc ceci :

public void afficheChamps()
{
    afficheChamps();
    System.out.println("Valeur de a1 = " + this.a1);
    System.out.println("Valeur de b1 = " + this.b1);
}

L' instruction afficheChamps(); définie dans la méthode afficheChamps(); de la classe Derive1, ne cherchera pas la méthode afficheChamps(); de la classe Base. Mais la méthode afficheChamps(); de la classe Derive1. En effet, lorsque le compilateur trouve l' appel d' une méthode, il cherche d' abord cette méthode dans la classe où se trouve l' appel. S' il ne la trouve pas, alors, il cherche dans la classe de base. J' entends par là, la classe superieure immédiate. Donc, la classe mère. Si la méthode n' y est pas trouvée non plus, il cherche dans la classe grand-mère. Ainsi de suite jusqu' à ce qu' il trouve. Et s' il ne trouve pas, un message d' erreur est renvoyé.
Or, ici, il trouve la méthode afficheChamps() dans la classe Derive1. Donc, le compilateur n' ira jamais dans la classe mère. Et ici, on a affaire à une méthode qui s' appelle elle-ême. On appelle cela, la récursivité. C' est une notion à manipuler avec précaution. On verra quelques exemples dans le chapitre Exercices de POO : héritage et polymorphisme.

Alors, que faire pour aller chercher la méthode afficheChamps() de la classe Base ? ... on utilise le mot clé super.
Voyons cela dans l' exemple 12 ci-dessous. C' est l' exmple 10 modifié.
Exemple 12 : Les constructeurs des classes dérivées successives.
Cas où toutes les classes ont un constructeur 

La classe de base, la classe dérivée et la classe dérivée 2ème possèdent chacune un constructeur.


1.     class CompteBancaire10
2.     {
3.     
4.          private String nom, prenom, adresse, tel;
5.          
6.          public CompteBancaire10(String nom, String prenom, String adresse, String tel)
7.          {
8.              this.nom = nom; this.prenom = prenom; this.adresse = adresse;
9.              this.tel = tel;
10.         }
11.         
12          public void afficherChamps()
13.         {
14.               System.out.println("Nom du titulaire = " + this.nom);
15.               System.out.println("Prenom du titulaire = " + this.prenom);
16.               System.out.println("Adresse du titulaire = " + this.adresse);
17.               System.out.println("Téléphonne du titulaire = " + this.tel);
18.         }
19.    }
20.     
21.    class CompteEpargne13 extends CompteBancaire10
22.    {
23.         private double soldeMinimal;
24.          
25.         public CompteEpargne13(String nom, String prenom, String adresse, String tel, double soldeMinimal)
26.         {
27.              super(nom, prenom, adresse, tel);
28.              this.soldeMinimal = soldeMinimal;
29.         }                   
30.         
31.         public void afficherChamps()
32.         {
33.              super.afficherChamps();
34.              System.out.println("Solde minimal du titulaire = " + this.soldeMinimal);
35.         }
36.    }
37.    
38.    class LivretA2 extends CompteEpargne13
39.    {
40.         private double plafond;
41.         
42.         public LivretA2(String nom, String prenom, String adresse, String tel, double soldeMinimal, double plafond)
43.         {
44.             super(nom, prenom, adresse, tel, soldeMinimal);
45.             this.plafond = plafond;
46.         }
47.
48.         public void afficherChamps()
49.         {
50.              super.afficherChamps();
51.              System.out.println("plafond du titulaire = " + this.plafond);
52.         }
53.   }
54.
55.
56.    public class TesterConstructeursDerives7
57.    {
58.          public static void main(String [] args)
59.          {
60.               LivretA2 LA2 = new LivretA("BELL", "Paul", "2 Rue du lac 75000 PARIS", "0606060606", 255.00, 115000.00);
61.               LA2.afficherChamps();
62.          }
63.    }     


Enregistrez ce fichier sous TesterConstructeursDerives7.java

compilez puis exécutez ce programme, il sera affiché :

Nom du titulaire = BELL
Prenom du titulaire = Paul
Adresse du titulaire = 2 Rue du lac 75000 PARIS
Télphonne du titulaire = 0606060606
Solde minimal du titulaire = 255.0
plafond du titulaire = 115000.0

Explications :

Dans la programme TesterConstructeursDerives7.java, l' instruction en ligne 60 permet de créer un objet de type LivretA2.
En ligne 61, j' applique à cet objet (LA2), la méthode afficherChamps(). C' est forcément la méthode afficherChamps() définie dans la classe LivretA2. Puisque l' objet LA2 a été instancié à partir de cette classe.
Cette méthode, définie ligne 48 a pour première instruction, super.afficherChamps().
Ce qui veut dire que la méthode afficherChamps() de la classe LivretA2 appelle la méthode afficherChamps() de la classe mère de LivretA2. Et cette classe est CompteEpargne13. Cette méthode est déclarée ligne 31. Sa première instruction est super.afficherChamps().
Ce qui veut dire que la méthode afficherChamps() de la classe CompteEpargne13 appelle la méthode afficherChamps() de la classe mère de CompteEpargne13 C' est à dire, la classe CompteBancaire10. Toutes les instructions de la méthode afficherChamps() de la classe CompteBancaire10 sont donc exécutées dans la méthode afficherChamps() de la classe CompteEpargne13 + l' instruction de la ligne 28. Donc, affichage des champs de la classe CompteBancaire10 et celui de la classe CompteEpargne13.
Ensuite, l' appel super.afficherChamps() de la méthode afficherChamps() de la classe LivretA2 signifie que Toutes les instructions de la méthode afficherChamps() de la classe CompteEpargne13 sont exécutées dans la méthode afficherChamps() de la classe LivretA2.
En fin de compte, l' instruction de la ligne 61 : LA2.afficherChamps() permet alors d' afficher tous les champs de toutes les classes.

Je sens que vous vous posez la question de savoir pourquoi se compliquer la vie à ce point puisque le programme de l' exemple 10 permet d' aboutir au même résultat. Et vous aurez raison de vous poser cette question. Mais si je vous ai montré cette façon de faire, c' est pour arriver à une notion très importante en java : la redéfinition d' une méthode.
A ne pas confondre avec la surdéfinition d' une méthode

Surdéfinir une méthode, c' est écrire la même méthode en plusieurs versions dans la même classe. D' une version à l' autre, la signature change. La signature d' une méthode, je le rappelle, c' est le nombre et le type des arguments.

La redéfinition d' une méthode, c' est l' écriture d' une méthode en plusieurs exemplaires, mais dans des classes différentes. Si ces classes différentes n' ont aucun lien, ça n' a pas d' intérêt. Mais si ces classes, qui contiennent chacune, une version de la méthode, sont liées par la notion d' héritage, alors là, l' intérêt se manifeste. Les puriste en programmation, considèrent d' ailleurs que c' est le seul cas où il faut parler de redéfinition

Pour la redéfinition, la signature des méthodes ne change pas. Y compris le type de retour.

Voici la méthode marcher() dans une classe de base.

public double marcher(int nombreDePattes)
{
   ...
{


La voici dans la classe dérivée

public void marcher(int nombreDePattes)
{
    ...
{


Ici, on ne peut pas parler de redéfinition. Le type de la méthode est différente d' une classe à l' autre. Dans la classe de base, elle est de type double. Alors que dans la classe dérivée, elle est de type void. Même si la signature est la même, on ne parlera pas de redéfinition.

Revenons sur le programme pour bien spécifier le mot clé super

Dans un constructeur, on utilise le mot clé super pour appeler le constructeur de la classe de base. La classe mère. On écrit alors le mot super, suivi des parenthèses à l' intérieur desquelles on a la signature du contructeur appelé.(signature = nombre et type d' arguments).
Dans une méthode quelconque (qui n' est pas contructeur), on utilise le mot super pour appeler une méthode de la classe de base, ayant le même nom que la méthode dans laquelle se trouve ce mot super. On écrit alors le mot super, suivi d' un point, suivi du nom de la méthode appelée.
Dans les exemples suivants, nous verrons l' impact de la redéfinition et de la surdéfinition sur les dérivations successives ou multiples.
Exemple 13 : Redéfinitions dans des dérivations successives. 

1.     class Base
2.     {
3.          public void marcher()
4.          {
5.               System.out.println("marcher dans la base");
6.          }
7.     }
8.    
9.  
10.    class Derive1 extends Base
11.    {
12.     
13.    }
14. 
15.
16.    class Derive2 extends Derive1
17.    {
18.        public void marcher()
19.        {
20.            System.out.println("marcher dans la derive2");
21.        }
22.    }
23.
24.    
25.    class Derive3 extends Derive2
26.    {
27.        
28.    }
29.
30.
31.    class Derive4 extends Derive3
32.    {
33.     
34.    }
35.
36.
37.    public class TesterRedefinitionsDansDerivationsSuccessives
38.    {
39.        public static void main(String [] args)
40.        {
41.             Base b = new Base();
42.             b.marcher();
43.             Derive1 d1 = new Derive1();
44.             d1.marcher();
45.             Derive2 d2 = new Derive2();
46.             d2.marcher();
47.             Derive3 d3 = new Derive3();
48.             d3.marcher();
49.             Derive4 d4 = new Derive4();
50.             d4.marcher();
51.        }
52.    }
Enregistrez ce fichier sous TesterRedefinitionsDansDerivationsSuccessives.java

Complier puis exécutez ce programme. Il sera affiché :

marcher dans la base
marcher dans la base
marcher dans la derive2
marcher dans la derive2
marcher dans la derive2


Ligne 41, je crée un objet à partir de la classe de base (Base). Ligne 42 : J' applique à cet objet, la méthode marcher(). Le compilateur cherche alors la méthode marcher() dans la classe Base. Et la méthode s' y trouve avec l' instruction permettant d' afficher marcher dans la base. D' où l' affichage de cette expression.

Ligne 43, je crée un objet à partir de la classe Derive1 . Ligne 44 : J' applique à cet objet, la méthode marcher(). Le compilateur cherche alors la méthode marcher() dans la classe Derive1. La méthode ne s' y trouve pas. Alors, le compilateur va chercher la méthode dans la classe mère de Derive1. C' est la classe Base. Et la méthode s' y trouve avec l' instruction permettant d' afficher marcher dans la base. D' où l' affichage de cette expression.

Ligne 45, je crée un objet à partir de la classe de base (Derive2). Ligne 46 : J' applique à cet objet, la méthode marcher(). Le compilateur cherche alors la méthode marcher() dans la classe Derive2. Et la méthode s' y trouve avec l' instruction permettant d' afficher marcher dans la derive2. D' où l' affichage de cette expression.

Ligne 47, je crée un objet à partir de la classe Derive3 . Ligne 48 : J' applique à cet objet, la méthode marcher(). Le compilateur cherche alors la méthode marcher() dans la classe Derive3. La méthode ne s' y trouve pas. Alors, le compilateur va chercher la méthode dans la classe mère de Derive3. C' est la classe Derive2. Et la méthode s' y trouve avec l' instruction permettant d' afficher marcher dans la derive2. D' où l' affichage de cette expression.

Ligne 49, je crée un objet à partir de la classe Derive4 . Ligne 48 : J' applique à cet objet, la méthode marcher(). Le compilateur cherche alors la méthode marcher() dans la classe Derive4. La méthode ne s' y trouve pas. Alors, le compilateur va chercher la méthode dans la classe mère de Derive4. C' est la classe Derive3.La méthode ne s' y trouve pas.
Alors, le compilateur va chercher la méthode dans la classe mère de Derive3. C' est la classe Derive2. Et la méthode s' y trouve avec l' instruction permettant d' afficher marcher dans la derive2. D' où l' affichage de cette expression.

Ceci est un principe général. Que nous soyons d' ailleurs en situation de redéfinition d' une méthode ou pas.

Lorsqu' on applique à un objet, une méthode, le compilateur cherche la méthode dans la classe à partir de laquelle l' objet a été instancié. Si la méthode ne s' y trouve pas, c' est dans la classe mère que cette méthode est recherchée. Si elle ne s' y trouve pas non plus, c' est dans la classe grand-mère que la méthode est recherchée. Ainsi de suite jusqu' à la classe où la méthode est trouvée.
Le compilateur fait toujours sa recherche dans les classes ascendantes (mère, grand-mère, arrière-grand-m-re, etc...). Et jamais dans les classes descendantes (fille, petite fille, arrière petite-fille, etc...).

Si la méthode n' existe dans aucune des classes ascendantes, une erreur est déclenchée à la compilation.
Maintenant, voyons le cas des surdéfinitions dans des dérivations successives.

Avant de voir ce que c' est, permettez moi de vous dire que la surdéfinition d' une méthode dans une même classe, c' est un classique en java. Vous en verrez beaucoup. Pour ce qui est de la surdéfinition d' une méthode à travers des classes dérivées, c' est moins fréquent. Pour ma part, j' ai dû essuyer pas mal d' échecs en tentant de surdéfinir une méthode dans diverses classes dérivées.
Si vous êtes débutants en java, je vous le déconseille dans l' immédiat. Attendez d' avoir un niveau un peu avancé pour tenter cela.

Pour ce qui est de l' action combinée de la redéfinition et de la surdéfinition, inutile de vous dire que parfois, c' est carrément un cauchemard. Je parle toujours du cas des débutants en java. Je vous conseillerais donc de surdéfinir une méthode dans une seule classe.
La redéfinition quant à elle, ne se fait qu' à travers plusieurs classes liées par l' héritage. Si vous sssayez de redéfinir une méthode dans une même classe, le compilateur vous renverra un message d' erreur. Normal. En redéfinition, les méthodes ont la même signature.
Exemple 14 : Surdéfinitions dans des dérivations successives. 


1.     class Base
2.     {
3.          public void marcher(int i)
4.          {
5.               System.out.println("marcher avec " + i + " pattes.");
6.          }
7.     }
8.    
9.  
10.    class Derive extends Base
11.    {
12.        public void marcher(short s)
13.        {
14.            System.out.println("marcher avec " + s + " pattes.");
13.        }
14.    }
15.
16.    public class TesterSurdefinitionsDansDerivationsSuccessives
17.    {
18.        public static void main(String [] args)
19.        {
20.            Base b = new Base();
21.            Derive d = new Derive();
22.            int i = 2; short s = 1000;
23.            b.marcher(i);
24.            b.marcher(s);
25.            d.marcher(s);
26.            d.marcher(i);
27.        }
28.    }
Enregistrez ce fichier sous TesterSurdefinitionsDansDerivationsSuccessives.java

compilez puis exécutez ce programme. Il sera affiché :

marcher avec 2 pattes.
marcher avec 1000 pattes.
marcher avec 1000 pattes.
marcher avec 2 pattes.

Ligne 20 : on créé un objet de type Base. Ligne 21 : on crée un objet de type Derive.
ligne 23 : on applique à l' objet b, la méthode marcher avec un argument de type int. ça tombe bien, la classe Base contient une méthode marcher() qui requiert un argument de type int. D' où l' affichage de marcher à) 2 pattes.
Ligne 24 : on applique à l' objet b, la méthode marcher avec un argument de type short. Il existe dans la classe Base, une méthode marcher() qui requiert un argument de type int. Mais l' argument de type short peut être implicitement converti en int. D' où l' affichage marcher à) 2 pattes.
. ligne 25 : on applique à l' objet d, une méthode marcher() avec un argument de type short. Cela tombe bien. La classe Derive contient une méthode marcher() qui requiert un argument de type short. D' où l' affichage marcher à) 1000 pattes.
. ligne 26 : on applique à l' objet d, une méthode marcher() avec un argument de type int. Mais la classe Derive ne possède pas de méthode marcher() avec un argument de type int. Mais plutôt avec un argument de type short. Et le type int ne peut pas être converti implicitement en short. Dans ce cas, le compilateur remonte la hiérachie des classes. Et dans la classe mère (Base), il existe une méthode marcher() qui requiert un argument de type int. C' est cette méthode marcher() qui est appelée. D' où l' affichage de marcher à 2 pattes.

Partie 2 : Polymorphisme

Exemple 15 : Polymorphisme


1.     class Primate
2.     {
3.          public void marcher()
4.          {
5.               System.out.println("marcher avec des pattes.");
6.          }
7.     }
8.    
9.  
10.    class Homme extends Primate
11.    {
12.        public void marcher()
13.        {
14.            System.out.println("marcher comme un homme.");
13.        }
14.    }
15.
16.    class Singe extends Primate
17.    {
18.        public void marcher()
19.        {
20.            System.out.println("marcher comme un singe.");
21.        }
22.    }
23.
24.
25.    public class TesterPolymorphisme1
26.    {
27.        public static void main(String [] args)
28.        {
29.            Primate p = new Primate();
30.            p.marcher();
31.            Homme h = new Homme();
32.            h.marcher();
33.            Singe s =  new Singe();
34.            s.marcher();
35.            System.out.println();
35.
36.            Primate p2 = new Homme();
37.            p2.marcher();
38.            Primate p3 = new Singe();
39.            p3.marcher();
40.        }
41.    }
Enregistrez ce programme sous TesterPolymorphisme1.java

Compilez puis exécutez. Il sera afficher :

marcher avec des pattes.
marcher comme un homme.
marcher comme un singe.

marcher comme un homme.
marcher comme un singe.

Explications :

En ligne 29, on crée un objet de type Primate. En ligne 30, on applique à cet objet la méthode marcher(). Et naturellement, c' est la méthode marcher() de la classe Primate qui est utilisée. D' où l' affichage : marcher avec des pattes.

En ligne 31, on crée un objet de type Homme. En ligne 32, on applique à cet objet la méthode marcher(). Et naturellement, c' est la méthode marcher() de la classe Homme qui est utilisée. D' où l' affichage : marcher comme un homme.

En ligne 33, on crée un objet de type Singe. En ligne 34, on applique à cet objet la méthode marcher(). Et naturellement, c' est la méthode marcher() de la classe Singe qui est utilisée. D' où l' affichage : marcher comme un singe.

En ligne 36, on crée un objet de type Homme (new Homme() ). Seulement, on met sa référence dans une variable de type Primate. Pourquoi ? ... Parce que un objet de type Homme est d' abord un objet de type Primate. Cela semble logique parce qu' effectivement un homme est forcément un primate.

Disons les choses de façon générale. Un objet de type dérivé est d' abord un objet de type de base. Nous savons qu' un objet de la classe dérivé dispose de toutes les méthodes et champs de la classe de base. Pour cette raison, il peut se comporter comme un objet de la classe de base. La réciproque n' est pas vraie.
Revenons à l' exemple ci-dessus.
Je peux donc créer un objet de la classe dérivée puis mettre sa référence dans une variable de la classe de base. Quel intérêt me direz-vous ?
Imaginez un tableau d' objets. Les objets en question sont issus d' une classe de base et de ses classes dérivées. Supposons 3 classes dérivées (dérivées successives ou multiples. Peu importe) : Derive1, Derive2, Derive 3. La classe de Base s' appelle Base.
Si je veux 12 objets dont 3 dans chaque classe, je ferais :

Base [] base = new Base [3];
base[0] = new Base(paramètres éventuels);
base[1] = new Base(paramètres éventuels);
base[2] = new Base(paramètres éventuels);
Création d' un tableau de 3 objets issus de la classe de base.

Même chose pour chacune des 3 classes dérivées. Mais je n' aurais pas tous les objets (issus de Base et des dérivées) dans un même tableau. Et pour cause, tous ces objets ne seront pas du même type.
3 seront de type Base. 3 seront de type Derive1. 3 seront de type Derive2. Et 3 seront de type Derive3.

Maintenant, exploitons le polymorphisme.

Base [] tab = new Base[12];
Création d' un tableau de références de type Base. Mais grâce au polymorphisme, les objets ne sont pas obligés d' être issus de la classe de base Base

tab[0] = new Base();
tab[1] = new Base();
tab[2] = new Base();
Ces trois objets sont de type Base. Et leurs références sont dans des variables de type Base.

tab[3] = new Derive1();
tab[4] = new Derive1();
tab[5] = new Derive1();
Ces trois objets sont de type Derive1. Et leurs références sont dans des variables de type Base.

tab[6] = new Derive2();
tab[7] = new Derive2();
tab[8] = new Derive2();
Ces trois objets sont de type Derive2. Et leurs références sont dans des variables de type Base.

tab[9] = new Derive3();
tab[10] = new Derive3();
tab[11] = new Derive3();
Ces trois objets sont de type Derive3. Et leurs références sont dans des variables de type Base.

Parce que leurs références sont toutes des variables de type Base, Ces objets peuvent tous faire partie d' un tableau d' objets (ici, tab) de type Base. Pigé ?
Revenons à l' exemple ci-dessus !

En ligne 36 donc, on crée un objet de type Homme (new Homme() ). Puis on met sa référence dans une variable de type Primate (p2). Puis en ligne 37, on applique la méthode marcher() à p2. Mais c' est la méthode marcher() de la classe Homme qui est appliquée. D' où l' affichage : marcher comme un homme.
Pourquoi ? ... Parce que p2 est la référence de l' objet de type Homme. Même si la variable p2 est de type Primate, c' est quand même l' objet de type Homme qui a été créé.

EN ligne 38, on crée un objet de type Singe (new Singe() ). Puis on met sa référence dans une variable de type Primate. En ligne 39, on applique la méthode marcher(). Et c' est la méthode marcher() de la classe Singe qui est sollicitée. D' où l' affichage : marcher comme un singe.

Voyons maintenant le cas des objets dans un tableau. Et vous comprendrez mieux l' intérêt du polymorphisme dans ce cas là.
Exemple 16 : Polymorphisme


1.     class Primate
2.     {
3.          public void marcher()
4.          {
5.               System.out.println("marcher avec des pattes.");
6.          }
7.     }
8.    
9.  
10.    class Homme extends Primate
11.    {
12.        public void marcher()
13.        {
14.            System.out.println("marcher comme un homme.");
13.        }
14.    }
15.
16.    class Singe extends Primate
17.    {
18.        public void marcher()
19.        {
20.            System.out.println("marcher comme un singe.");
21.        }
22.    }
23.
24.
25.    public class TesterPolymorphisme2
26.    {
27.        public static void main(String [] args)
28.        {
29.            //Cration d' un tableau de références de type Primate.
30.            Primate [] p = new Primate[12];
31.            
32.            //création des objets de la classe de base et des classes dérivées
33.               
34.            for(int i = 0; i < p.length; i++)
35.            {
36.                if(i < 4)
37.                p[i] = new Primate(); //création des objets de type Primate
38.                
39.                else if (i <= 4 || i < 8)
40.                p[i] = new Homme(); //Création des objets de type Homme
41.                
42.                else
43.                p[i] = new Singe(); //Création des objets de type Singe
44.           }
45.           
46.           //Ici, on applique la méthode marcher() à tous les objets du tableau.
47.           
48.           for(int i = 0; i < p.length; i++)
49.           {
50.                p[i].marcher(); 
51.           }
52.        }
53.    }

Enregistrez ce programme sous TesterPolymorphisme2.java

Compilez puis exécutez. Il sera afficher :

marcher avec des pattes.
marcher avec des pattes.
marcher avec des pattes.
marcher avec des pattes.
marcher comme un homme.
marcher comme un homme.
marcher comme un homme.
marcher comme un homme.
marcher comme un singe.
marcher comme un singe.
marcher comme un singe.
marcher comme un singe.

Explications :

Rappelons d' abord qu' en java, le double slash (//) veut dire que ce qui suit est un commentaire et n' est donc pas le code.

En ligne 30, je crée un tableau de variables de type Primate. Ces variables sont sensés recevoir les références (adresses) des objets de type Primate, et aussi (grâce au polymorphisme), des objets appartenant à toutes las classes dérivées de la classe Primate.

De la ligne 34 à la ligne 44, nous avons une boucle qui parcours les indices de zéro au dernier indice du tableau de références p. Lignes 36 et 37 : je dis que si cet indice est inférieur à 4, alors je créé un objet de type Primate. Ce qui veut dire 4 objets créés dont la référence se trouve dans l' une des variables de type Primate. En l' occurence, p[0], p[1], p[2] et p[3].

Lignes 39 et 40 : je dis que autrement, si cet indice est supérieur ou égal à 4 et inférieur à 8, alors je créé un objet de type Homme. Ce qui veut dire 4 objets créés dont la référence se trouve dans l' une des variables de type Primate. En l' occurence, p[4], p[5], p[6] et p[7].

Lignes 42 et 43 : je dis que autrement (sous-entendu, les autres cas : 8, 9, 10 et 11), alors je créé un objet de type Homme. Ce qui veut dire 4 objets créés dont la référence se trouve dans l' une des variables de type Primate. En l' occurence, p[8], p[9], p[10] et p[11].

Lignes 48 à 51 : nous avons une boucle qui parcours tous ces objets en leur appliquant la méthode marcher(). Ainsi, pour les objets de type Primate, c' est la méthode marcher() de la classe Primate qui sera appliquée. D' où l' affichage : marcher avec des pattes. : 4 fois pour les 4 objets Primates créés

Pour les objets de types Homme, c' est la méthode marcher() de la classe Homme qui sera appliquée. D' où l' affichage : marcher comme un homme. : 4 fois pour les 4 objets Homme créés

Pour les objets de types Singe, c' est la méthode marcher() de la classe Singe qui sera appliquée. D' où l' affichage : marcher comme un Singe. : 4 fois pour les 4 objets Singes créés

Vous voyez donc un intérêt formidable du polymorphisme. Mais il n' y a pas que cet intérêt là.
Le mot polymorphisme signifie : état de ce qui peut prendre plusieurs formes. Etat de tout ce qui est polymorphe. Polymorphe vient du grec. polybius = plusieurs et morpho = formes. Le polymorphisme est donc l' état de tout ce qui peut prendre plusieurs formes.

Vous créez une variable de type classe de base. Cette variable peut accepter les références de tout objet de la classe de base. Mais aussi des classes dérivées de cette classe de base. Ces objets ont donc diverses formes.
Rappel sur les conversions:

int a = 5; short b = 22;

L' expression a = b; est correcte. Parce que le compilateur converti d' abord b en int. Puis met sa valeur dans a. C' est de la conversion implicite. L' expression b = a; n' est pas correcte. un message d' erreur sera affiché à la compilation. Parce que le type int est plus étendu que le type short. Le message d' erreur sera possible loss of precision

Pour mettre le int dans le short, on procède à la conversion explicite. b = (short)a; Et dans ce cas, pas de message d' erreur. Mais possibilité d' une perte de précision.

Les objets aussi subissent des conversions implicites ou explicites. Et grâce à l' héritage et au polymorphisme, les conversions vont pouvoir se faire.
Exemple 17 : Conversion des objets


1.     class Animal
2.     {
3.          public void marcher()
4.          {
5.               System.out.println("marcher comme un animal");
6.          }
7.     }
8.    
9.  
10.    class Mamifere extends Animal
11.    {
12.        public void marcher()
13.        {
14.            System.out.println("marcher comme un mamifere.");
13.        }
14.    }
15.
16.    class Humain extends Mamifere
17.    {
18.        public void marcher()
19.        {
20.            System.out.println("marcher comme un humain.");
21.        }
22.    }
23.
24.
25.    public class ConversionsDesObjets
26.    {
27.        public static void main(String [] args)
28.        {
29.            Animal a = new Animal();
30.            Animal m = new Mamifere();
31.            Animal h = new Humain();
32.            
33.            Mamifere m2 = m;
34.            Humain h2 = h;
35.            
36.        }
37.    }          

Enregistrez ce programme sous : ConversionsDesObjets.java

Compilez ce programme, il sera affiché un message d' erreur :

ConversionsDesObjets.java:33: incompatible types
found : Animal
required: Mamifere
Mamifere m2 = m;
^
ConversionsDesObjets.java:34: incompatible types
found : Animal
required: Humain
Humain h2 = h;
^
2 errors

Ce qui veut dire qu' il y a 2 erreurs : l' une en ligne 33 et l' autre en ligne 34.

En ligne 33 : incompatibilité des types. On trouve un type Animal alors qu' on a demandé un type Humain.
En effet, Mamifere m2 = m; Cela veut dire qu' on essaye de mettre le type Animal (variable m) dans le type Mamifere(m2). Cela vous étonne que m soit de type Animal et pas de type Mamifere ? ... Ne soyez pas étonné. Regardez ligne 30 : Animal m = new Mamifere(); new Mamifere() est un objet de type Mamifere certes. Mais sa référence est mise dans une variable de type Animal qui est m ici. m est donc une variable de type Animal. On ne peut pas convertir un type issu de la classe de base en un type issu de la classe dérivée. Un animal n' est pas forcément un mamifere. Alors que la réciproque est vraie.
Mais dans l' écriture Animal m = new Mamifere(); il y a eu conversion implicite. De quoi ? ... d' une référence en une autre. Créer un objet (Exemple : new Mamifere() ), crée forcément une référence de type Mamifere. En mettant cette référence dans la variable m, on a implicitement converti la référence de new Mamifere(), de type Mamifere en la référence de type Animal. Et le passage de la référence de type classe dérivée en la référence de type classe de base se fait naturellement. Que dis-je ? se fait implicitement.

Vous utiliserez le même raisonnement pour l' erreur numéro 2.

Pour convertir la référence m en la référence m2, il faut la faire implicitement.
En l' occurence : Mamifere m2 = (Mamifere)m;
C' est la fameuse opération de cast, comme dans les variables de type primaire (int, long, byte, char, etc..). On a d' abord transformé la variable m de type Animal en type Mamifere avant d' affecter sa valeur à la variable m2 de type Mamifere.
De même, on fera Humain h2 = (Humain)h;

Vous l' aurez (déjà ?) compris, la conversion ne modifiant en aucun cas l' objet, seules les références sont converties. Pas les objets.
Exemple 18 : Conversion des objets

Reprenez le programme 17. Changer les instructions 
33.            Mamifere m2 = m;
34.            Humain h2 = h;

en 

33.            Mamifere m2 = (Mamifere)m;
34.            Humain h2 = (Humain)h;
Compilez. Aucun message d' erreur ne sera affiché. PAs la peine d' exécuter. Il ne s 'affichera rien du tout. Car aucune instruction d' affichage n' existe dans le programme.
La connaissance, c'est bien. La partager, c'est mieux
Conseiller ce site à un(e) ami(e):

Son e-mail est :       
Une suggestion à faire pour ce site ? ... Contact : webmaster@debutantprog.com
← (précédent) C.4 Exercices corrigés de P.O.O (1)
C.6 Exercices de P.O.O (2)
Accueil S O M M A I R E

C.5 Héritage et Polymorphisme