ICS4U

Accueil > Programmation orientée objet >

📚 Héritage et polymorphisme

Survol et attentes

En créant des logiciels plus complexes, plusieurs objets peuvent avoir des caractéristiques communes. Par exemple, un jeu vidéo peut avoir plusieurs types de personnages, mais tous les personnages ont des caractéristiques communes comme la position, la vitesse, la direction, etc. Dans ce cas, il est utile de créer une classe de base qui contient les caractéristiques communes, puis de créer des classes dérivées qui contiennent les caractéristiques spécifiques à chaque type de personnage.

Définitions

L’héritage nous perment de déclarer une classe parente qui contient des attributs et des méthodes communes à plusieurs classes enfants.

Les classes enfants héritent des attributs et des méthodes de la classe parente. Les classes enfants peuvent supplanter la définition d’une méthode de la classe parente, comme on l’a déjà vu avec les méthodes toString(), equals()ethashCode()`. Elles peuvent aussi avoir leurs propres attributs et méthodes.

Seulement les attributs de visibilité protected ou public d’une classe parent seront hérités par les enfants. La nouvelle visibilité protected est comme private sauf pour les membres de la même famille. Donc, les attributs private de la classe parent appartiennent uniquement au parent tandis que les attributs protected seront hérités.

Notez que la visibilité par défaut (sans déclaration explicite) est package-private qui est comme public à l’intérieur du package et comme private à l’extérieur. Vous ne pouvez pas explicitement déclarer quelque chose package-private… simplement n’utilisez pas de mot-clé si vous voulez ce niveau de visibilité.

Exemples et solutions

Une classe enfant indique son lien de parenté en utilisant le mot-clé extends dans sa déclaration. Par exemple, la classe Warrior peut être déclarée comme suit :

1
2
3
public class Warrior extends Player {
    // ...
}

Une classe enfant peut réutiliser le constructeur de sa classe parente en utilisant la méthode super() dans son constructeur. Par exemple, la classe Warrior peut être déclarée comme suit :

1
2
3
4
5
public class Warrior extends Player {
    public Warrior(String name, int health, int strength) {
        super(name, health, strength);
    }
}

présumant que la classe Player a déclarée un constructeur comme suit :

1
2
3
4
5
6
7
8
9
10
11
public class Player {
    protected String name;
    protected int health;
    protected int strength;

    public Player(String name, int health, int strength) {
        this.name = name;
        this.health = health;
        this.strength = strength;
    }
}

Le mot-clé super() s’applique aux autres méthodes de la classe parente également. S’il est utilisé, ça doit toujours être la première instruction dans la méthode de la classe enfant qui supplante la méthode originale.

Dans un diagramme de classe UML, on indique la relation d’héritage avec une ligne solide et une tête de flèche en triangle pointant vers la classe parente. Par exemple, les classes Warrior, Wizard et Healer héritent toutes de la classe Player :

heritage1

Relation “est un”

L’héritage est une relation “est un”. Par exemple, un Warrior est un Player. On peut donc utiliser un objet de type Warrior partout où un objet de type Player est attendu. Par exemple, on peut déclarer une variable de type Player et lui assigner un objet de type Warrior ou à d’autres types qui sont des extensions de Player :

1
2
3
4
ArrayList<Player> players = new ArrayList<>();
players.add(new Warrior("Conan", 90, 10));
players.add(new Wizard("Gandalf", 50, 5));
players.add(new Healer("Rowena", 100, 2));

La liste est déclarée comme une liste de Player, mais on peut y ajouter des objets de type Warrior, Wizard et Healer parce que ces classes “sont des” Player via l’héritage. Quand les objets de type parent pointent à des instances de type enfant, cela s’appelle le polymorphisme. Le polymorphisme fait spécifiquement référence à la capacité d’appeler la même méthode sur des objets de types différents et d’obtenir des résultats différents : cette méthode a plusieurs formes, littéralement la définition de polymorphisme.

La vidéo suivante présente un bon portrait du polymorphisme possible via l’héritage, soit celui qui est possible en prenant des types enfants pour le type parent : 📺 Upcasting et downcasting

Objectifs d’apprentissage

À la fin de cette leçon vous devrez être en mesure de :

Critères de succès

Exercices

Quiz de validation des connaissances

Travail pratique :

  1. Créer les fichiers appropriés pour les classes suivantes et remarquez ce qui se passe en lançant la classe App :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
     public class Parent {
         String name;
    
         Parent(String name) {
             this.name = name;
         }
    
         void role() {
             System.out.print("I'm the ");
         }
    
         void sayName() {
             System.out.println("My name is " + name);
         }
    
     }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    
     public class Dad extends Parent {
    
         Dad(String name) {
             super(name);
         }
    
         @Override
         void role() {
             super.role(); // toujours en premier... réutilise le code de la classe parente
             System.out.println("dad.");
         }
    
         @Override // supplante (prend priorité sur) la version dans la classe parente
         void sayName() {
             System.out.println("My name is Mr. " + name + ".");
         }
     }
    
    1
    2
    3
    4
    5
    6
    7
    
     public class App {
         public static void main(String[] args) {
             Dad dad = new Dad("John");
             dad.role();
             dad.sayName();
         }
     }
    
  2. Créer deux classes Mom et LegalGuardian qui héritent de Parent et qui modifient le code hérité de manière appropriée. Ajouter des déclarations dans la classe App pour tester votre code.
  3. Préparer un diagramme de classe UML pour les classes Parent, Dad, Mom et LegalGuardian et vérifier votre diagramme avec votre enseignant(e).