↑
CHAPITRE 6.2

Polymorphisme

Redéfinir et surcharger les méthodes
Le polymorphisme permet à un objet de prendre plusieurs formes. En Java, cela se fait via la redéfinition (overriding) et la surcharge (overloading) de méthodes.

6.2Polymorphisme

6.2.1 – Redéfinition (overriding)

La redéfinition (ou overriding) permet à une classe fille de fournir une implémentation spécifique d'une méthode qui existe déjà dans la classe mère. La méthode redéfinie doit avoir la même signature (nom, paramètres, type de retour) que la méthode de la classe mère.

🔑 Principe de la redéfinition

Quand une classe fille redéfinit une méthode de la classe mère, la version de la classe fille est utilisée lorsque la méthode est appelée sur un objet de la classe fille.

Exemple : Redéfinition simple

public class Personne {
    public void sePresenter() {
        System.out.println("Je suis une personne");
    }
    
    public void travailler() {
        System.out.println("Je travaille");
    }
}

public class Etudiant extends Personne {
    @Override
    public void sePresenter() {
        System.out.println("Je suis un étudiant");
    }
    
    // travailler() n'est pas redéfinie, donc utilise la version de Personne
}

// Utilisation
Personne p = new Personne();
p.sePresenter();  // Affiche : Je suis une personne

Etudiant e = new Etudiant();
e.sePresenter();  // Affiche : Je suis un étudiant (version redéfinie)
e.travailler();   // Affiche : Je travaille (version héritée)

📝 Annotation @Override

L'annotation @Override indique explicitement que vous redéfinissez une méthode. Elle est optionnelle mais fortement recommandée car elle :

  • AmĂ©liore la lisibilitĂ© du code
  • Permet au compilateur de dĂ©tecter les erreurs (si la signature ne correspond pas)
  • Documente l'intention de redĂ©finir une mĂ©thode

Exemple : Avec et sans @Override

public class Etudiant extends Personne {
    // Sans @Override (fonctionne mais moins clair)
    public void sePresenter() {
        System.out.println("Je suis un étudiant");
    }
    
    // Avec @Override (recommandé)
    @Override
    public void sePresenter() {
        System.out.println("Je suis un étudiant");
    }
}

⚖️ Règles de redéfinition

Pour qu'une méthode soit correctement redéfinie, elle doit respecter ces règles :

  • MĂŞme signature : Nom, paramètres et type de retour identiques
  • VisibilitĂ© : La mĂ©thode redĂ©finie ne peut pas ĂŞtre moins visible que celle de la classe mère
  • Exceptions : La mĂ©thode redĂ©finie ne peut pas lever plus d'exceptions que celle de la classe mère
  • final : Une mĂ©thode final ne peut pas ĂŞtre redĂ©finie

Exemple d'erreur :

public class Personne {
    public void sePresenter() { }
}

public class Etudiant extends Personne {
    // ❌ ERREUR : type de retour différent
    // public int sePresenter() { return 0; }
    
    // ❌ ERREUR : paramètres différents (c'est une surcharge, pas une redéfinition)
    // public void sePresenter(String message) { }
    
    // âś… CORRECT : mĂŞme signature
    @Override
    public void sePresenter() { }
}
Important : Respectez les règles de redéfinition pour éviter les erreurs de compilation.

🔄 Appel de la méthode parente avec super

Dans une méthode redéfinie, vous pouvez appeler la version de la classe mère avec super pour étendre le comportement plutôt que de le remplacer complètement.

Exemple : Extension du comportement

public class Personne {
    public void sePresenter() {
        System.out.println("Je suis une personne.");
    }
}

public class Etudiant extends Personne {
    private String numeroEtudiant;
    
    @Override
    public void sePresenter() {
        super.sePresenter();  // Appelle la méthode de Personne
        System.out.println("Mon numéro étudiant est : " + numeroEtudiant);
    }
}

// Utilisation
Etudiant e = new Etudiant();
e.numeroEtudiant = "E12345";
e.sePresenter();
// Affiche :
// Je suis une personne.
// Mon numéro étudiant est : E12345

6.2.2 – Surcharge (overloading)

La surcharge (ou overloading) permet de définir plusieurs méthodes avec le même nom mais avec des paramètres différents (nombre, type, ordre). Le compilateur choisit la méthode appropriée en fonction des arguments passés.

🔑 Principe de la surcharge

La surcharge permet d'avoir plusieurs versions d'une méthode pour différents types de données ou nombres de paramètres, tout en gardant un nom cohérent.

Exemple : Surcharge de méthodes

public class Calculatrice {
    // Version 1 : deux entiers
    public int additionner(int a, int b) {
        System.out.println("Addition de deux entiers");
        return a + b;
    }
    
    // Version 2 : deux décimaux
    public double additionner(double a, double b) {
        System.out.println("Addition de deux décimaux");
        return a + b;
    }
    
    // Version 3 : trois entiers
    public int additionner(int a, int b, int c) {
        System.out.println("Addition de trois entiers");
        return a + b + c;
    }
    
    // Version 4 : un tableau d'entiers
    public int additionner(int[] nombres) {
        System.out.println("Addition d'un tableau");
        int somme = 0;
        for (int n : nombres) {
            somme += n;
        }
        return somme;
    }
}

// Utilisation
Calculatrice calc = new Calculatrice();
calc.additionner(5, 3);                    // Appelle la version 1
calc.additionner(5.5, 3.2);               // Appelle la version 2
calc.additionner(1, 2, 3);                 // Appelle la version 3
calc.additionner(new int[]{1, 2, 3, 4});   // Appelle la version 4

📋 Règles de surcharge

Pour qu'une méthode soit considérée comme une surcharge, elle doit :

  • Avoir le mĂŞme nom que les autres mĂ©thodes
  • Avoir des paramètres diffĂ©rents (nombre, type, ou ordre)
  • Le type de retour peut ĂŞtre diffĂ©rent, mais ce n'est pas suffisant pour crĂ©er une surcharge
Important : Le type de retour seul ne suffit pas pour créer une surcharge. Les paramètres doivent être différents.

Exemple : Surcharge valide et invalide

public class Exemple {
    // ✅ VALIDE : paramètres différents
    public int calculer(int a, int b) { return a + b; }
    public double calculer(double a, double b) { return a + b; }
    public int calculer(int a, int b, int c) { return a + b + c; }
    
    // ❌ ERREUR : même signature, seul le type de retour diffère
    // public int calculer(int a, int b) { return a + b; }
    // public double calculer(int a, int b) { return a + b; }  // ERREUR !
}

🔄 Surcharge vs Redéfinition

Il est important de distinguer la surcharge (overloading) de la redéfinition (overriding) :

Caractéristique Surcharge (Overloading) Redéfinition (Overriding)
Où Même classe Classe mère et classe fille
Signature Paramètres différents Même signature
Type de retour Peut être différent Doit être identique
Visibilité Peut être différente Ne peut pas être moins visible
@Override Non utilisé Recommandé
Résolution À la compilation À l'exécution (polymorphisme)

đź’ˇ Exemples pratiques de surcharge

Exemple : Constructeurs surchargés

public class Personne {
    private String nom;
    private int age;
    
    // Constructeur 1 : sans paramètres
    public Personne() {
        this.nom = "Inconnu";
        this.age = 0;
    }
    
    // Constructeur 2 : avec nom seulement
    public Personne(String nom) {
        this.nom = nom;
        this.age = 0;
    }
    
    // Constructeur 3 : avec nom et âge
    public Personne(String nom, int age) {
        this.nom = nom;
        this.age = age;
    }
}

Exemple : Méthodes utilitaires surchargées

public class Utilitaire {
    // Afficher un entier
    public void afficher(int valeur) {
        System.out.println("Entier : " + valeur);
    }
    
    // Afficher un décimal
    public void afficher(double valeur) {
        System.out.println("Décimal : " + valeur);
    }
    
    // Afficher une chaîne
    public void afficher(String valeur) {
        System.out.println("Chaîne : " + valeur);
    }
    
    // Afficher un tableau
    public void afficher(int[] valeurs) {
        System.out.print("Tableau : ");
        for (int v : valeurs) {
            System.out.print(v + " ");
        }
        System.out.println();
    }
}

💡 Points clés à retenir

  • Surcharge : MĂŞme nom, paramètres diffĂ©rents (mĂŞme classe)
  • RedĂ©finition : MĂŞme signature, implĂ©mentation diffĂ©rente (classe mère/fille)
  • RĂ©solution : La surcharge est rĂ©solue Ă  la compilation, la redĂ©finition Ă  l'exĂ©cution
  • Type de retour : Peut ĂŞtre diffĂ©rent pour la surcharge, doit ĂŞtre identique pour la redĂ©finition
  • @Override : UtilisĂ© pour la redĂ©finition, pas pour la surcharge
Conseils pratiques :
  • Utilisez la surcharge pour offrir plusieurs façons d'appeler une mĂ©thode avec diffĂ©rents types de donnĂ©es
  • La surcharge amĂ©liore la lisibilitĂ© en gardant un nom cohĂ©rent pour des opĂ©rations similaires
  • Ne confondez pas surcharge et redĂ©finition : la surcharge est dans la mĂŞme classe, la redĂ©finition entre classes parentes et filles
  • Les constructeurs sont souvent surchargĂ©s pour offrir diffĂ©rentes façons d'initialiser un objet