Blog ENI : Toute la veille numérique !
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
Accès illimité 24h/24 à tous nos livres & vidéos ! 
Découvrez la Bibliothèque Numérique ENI. Cliquez ici
  1. Livres et vidéos
  2. Apprendre la Programmation Orientée Objet avec le langage Java
  3. Héritage et polymorphisme
Extrait - Apprendre la Programmation Orientée Objet avec le langage Java (avec exercices pratiques et corrigés) (3e édition)
Extraits du livre
Apprendre la Programmation Orientée Objet avec le langage Java (avec exercices pratiques et corrigés) (3e édition) Revenir à la page d'achat du livre

Héritage et polymorphisme

Comprendre l’héritage

Le mécanisme de l’héritage est largement utilisé en programmation orientée objet et il est important d’en rappeler l’utilité.

Hériter d’une classe revient à "spécialiser" certains de ses comportements et certaines de ses propriétés tout en profitant de ses services de base et ainsi, éviter toute redondance de code.

Java et C# ne permettent qu’un seul héritage par niveau (contrairement à C++) mais il est possible d’hériter d’une classe elle-même déjà héritière et ainsi de suite pour constituer une hiérarchie de classes partant de la plus globale à la plus détaillée.

Exemple de hiérarchie de classes

images/06RI00.png

Par défaut, une classe peut servir de "parent" à plusieurs classes. Le meilleur exemple est, sans aucun doute, java.lang.Object qui est la classe racine de tous les types - et donc de toutes les classes - de Java. Rappelons que cet héritage est implicite et ne nécessite aucune déclaration particulière.

Codage de la superclasse (classe de base) et de sa sous-classe (classe héritière)

Le code d’une classe définit les règles de son éventuel héritage.

1. Interdire l’héritage

Tout d’abord est-il souhaitable de rendre une classe "héritable" ? Si l’analyse démontre que non alors le mot-clé final doit être utilisé dans la définition de la classe pour en interdire tout héritage.

Syntaxe de déclaration d’une classe "finale"

[visibilité] final class NomClasse 
{ 
        //... 
} 

Exemple de classe "finale"

    public final class Directeur { 
        //... 
    } 

Comme le montre la capture suivante, IntelliJ IDEA refuse de compiler une classe étendant la classe finale Directeur :

images/06R01IV3.png

La classe Java String est une classe de type "final".

Une classe ne contenant pas le mot-clé final dans sa définition est considérée comme extensible.

2. Définir les membres héritables

Une superclasse choisit ses membres "transmissibles" grâce à leurs attributs d’accessibilité. Ainsi, les classes héritières (sous-classes) auront le droit d’utiliser et de redéfinir les membres de type protected et, bien sûr, les membres...

Communication entre classe de base et classe héritière

1. Les constructeurs

Lorsqu’une classe héritière est instanciée, le constructeur de sa superclasse est appelé avant le sien. Voici un extrait de code suivi du résultat console qui en témoigne.

package com.eni; 
 
// Le point d'entrée de notre exemple 
public class Main { 
 
    public static void main(String[] args) { 
 
        DemoHeritage dh = new DemoHeritage(); 
        dh.Test(); 
    } 
} 

package com.eni; 
 
// La classe Demoheritage va... 
public class DemoHeritage { 
    // ... au travers de sa méthode Test 
    public void Test(){ 
        //... montrer l'instanciation de l'héritière 
        System.out.println("Instanciation d'une ClasseEnfant"); 
        ClasseEnfant classeEnfant = new ClasseEnfant(); 
        // 
    } 
} 

package com.eni; 
 
// Définition d'une classe héritière 
// de la superclasse ClasseParent 
public class ClasseEnfant extends ClasseParent { 
    // avec des propriétés public, protected et private 
    public String PublicPropClasseEnfant; 
    protected String ProtectedPropClasseEnfant; 
    private String PrivatePropClasseEnfant; 
 
    public ClasseEnfant() { 
        System.out.println("Ctor Classe Enfant"); 
    } 
 
} 

package com.eni; 
 
// Définition de la "superclasse"... 
 public class ClasseParent { 
    // avec des propriétés public, protected et private 
    public String PublicPropClasseParent; 
    protected String ProtectedPropClasseParent; 
    private String PrivatePropClasseParent; 
 
    public ClasseParent() { 
        System.out.println("Ctor Classe Parent"); 
    } 
} 

Sortie console correspondante :...

Exercice

1. Énoncé

 Créez une nouvelle solution de type console.

 Ajoutez une classe CompteBancaire représentant un… compte bancaire et ayant les propriétés suivantes :

  • Titulaire (String).

  • Numéro (Integer contenant une valeur unique attribuée à l’instanciation ; le premier numéro de compte sera 100).

  • Solde (double).

Et les méthodes suivantes :

  • Créditer (permet de déposer de l’argent sur le compte).

  • Débiter (permet de retirer de l’argent du compte).

  • Relever (affiche toutes les informations sur le compte).

 Ajoutez une classe CompteBancaireRemunere représentant un compte bancaire rémunéré héritant de la classe précédemment créée et dont le constructeur prendra comme paramètres le nom du titulaire et le taux de rendement du compte.

 Redéfinissez la méthode Créditer de CompteBancaireRemunere pour que le montant déposé soit augmenté du taux de rémunération défini dans le constructeur. Ne rêvons pas, ce fonctionnement bancaire atypique est uniquement fait pour simplifier l’exercice.

 Codez dans le main une séquence permettant de vérifier le fonctionnement des classes.

2. Corrigé

package labcomptebancaire; 
 
public class CompteBancaire { 
 
    // Entier de type static mémorisant 
    // le compteur global de numéro de compte 
    private static Integer numCount = 100; 
 
    // Nom du titulaire 
    private String Titulaire; 
    public String getTitulaire() { 
        return Titulaire;  
    }  
    public final void setTitulaire(String Titulaire) { 
       ...

Les classes abstraites

Il peut arriver qu’une superclasse contienne des méthodes ne pouvant être implémentées car n’ayant aucun sens sans un minimum de spécialisation…

Par exemple, une classe de base FormeGeometrique propose une méthode virtuelle Dessiner. Cette classe est ensuite étendue par les classes Triangle, Rectangle et Cercle qui vont remplacer et implémenter chacune leur propre méthode Dessiner.

L’implémentation de la méthode Dessiner dans la classe de base FormeGeometrique n’a donc aucun sens car chaque forme est spécifique et, au niveau de FormeGeometrique, cette forme est tout à fait abstraite !

Alors, dans ce cas, pourquoi définir la méthode Dessiner dans FormeGeometrique ?

Ceci est tout à fait en rapport avec le polymorphisme… Imaginez que vous soyez en train de construire une application de dessin et que cette application gère une série de formes géométriques soigneusement définies et enregistrées par l’utilisateur. Vous pourriez gérer une liste d’objets de type Triangle, une liste d’objets de type Rectangle, etc. Bon courage car cela deviendra très vite lourd… En construisant une liste d’objets de type FormeGeometrique, vous pourrez la remplir d’objets de types Triangle, Rectangle et Cercle. Pourquoi ? Parce...

Le polymorphisme

1. Comprendre le polymorphisme

En programmation orientée objet, le polymorphisme permet à une classe héritière d’être présentée dans un traitement comme sa classe de base ou comme une de ses interfaces. Grâce à la virtualisation des méthodes, le traitement appelant la méthode de base est "routé" dans la classe héritière. On obtient ainsi une "spécialisation" du traitement.

Toute classe dérivée peut implicitement être convertie en un objet de sa classe de base.

En langage Java, la virtualisation par défaut automatique a été détaillée dans les pages précédentes. Elle permet, d’une part à la classe de base de définir quelles méthodes peuvent être spécialisées par ses héritières, et d’autre part à son ou ses héritières de reprendre à leurs comptes les méthodes pour les spécialiser. 

En langage Java chaque objet est "polymorphe". Il peut en effet être considéré comme son propre type ou comme celui de sa classe de base et par bonds successifs comme le type racine de tous les types à savoir java.lang.Object.

2. Exploitation du polymorphisme

La programmation la meilleure est celle qui privilégie le moins...