ICS4U

Accueil > Programmation orientée objet >

📚 Interfaces et classes abstraites

Survol et attentes

L’héritage via un lien de parenté est bon pour des objets similaires qui accomplissent une tâche semblable. Mais que faire si l’objet parent ne sera jamais utilisé pour déclarer un objet? Ou si on veut un nouvel type d’objet qui n’utilise qu’une partie de la fonctionnalité d’une autre classe ou qui veut combiner la fonctionnalité de plusieurs classes?

Les réponses sont les classes abstraites et les interfaces.

Définitions

Une classe abstraite ne peut jamais être instanciée. Elle sert de modèle pour d’autres classes qui héritent de ses attributs et méthodes. Une classe abstraite peut contenir des méthodes abstraites, qui sont des méthodes qui n’ont pas de corps. Les classes qui héritent d’une classe abstraite doivent implémenter les méthodes abstraites. Une classe qui extends une classe abstraite respecte toujours la relation “est un” avec la classe abstraite, pareil que pour l’héritage normal.

Une interface est un type de classe qui ne contient que des méthodes abstraites (et parfois des constantes privées). Une classe peut implémenter plusieurs interfaces, mais ne peut hériter que d’une seule classe abstraite.

La vidéo suivante explique bien le portrait : 📺 Classes et méthodes abstraites

Classes abstraites

La classe Animal est un bon candidat pour une classe abstraite parce qu’aucun animal n’est juste un animal. Il y a des chiens, des chats, des oiseaux, etc. Pour déclarer que la classe Animal est abstraite, on utilise le mot-clé abstract :

1
2
3
4
5
6
7
8
public abstract class Animal { // abstract ici prévient l'instanciation de la classe
    protected String name;
    //...

    abstract void family(); // ; au lieu de {}
        // abstract ici force les classes enfants à implémenter cette méthode
    //...
}

Une classe enfant, p. ex. Dog, doit implémenter toutes les méthodes abstraites de la classe parente :

1
2
3
4
5
6
7
8
public class Dog extends Animal {
    //...
    @Override
    void family() {
        System.out.println("Canine");
    }
    //...
}

Souvent, les outils de votre EDI vous signalent une erreur en déclarant l’héritage d’une classe abstraite et vous proposent d’implémenter les méthodes abstraites. Dans VS Code, ce serait une suggestion parmis les “Quick Fix (Ctrl+.)”.

Voici le diagramme de classe UML simplifié (ne montrant pas le contenu des classes) pour la relation entre le parent abstrait Animal et l’enfant Dog :

abstract

Notez que c’est le même type de flèche parce que la relation est toujours de l’héritage, soit Dog est un Animal. La différence est l’apparence de la classe parente, qui est en italique pour indiquer qu’elle est abstraite.

Interfaces

Pour les interfaces, on remplace le mot-clé class par interface :

1
2
3
public interface Eating {
    void eat(); // méthode abstraite par défaut : pas besoin du mot-clé abstract
}
1
2
3
public interface Moving {
    void move();
}

Une classe peut implémenter plusieurs interfaces. Pour indiquer que la classe implémente une interface, on utilise le mot-clé implements. S’il y a plusieurs interfaces, on les sépare par des virgules :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Dog extends Animal implements Eating, Moving {
    //...
    @Override
    void family() {
        System.out.println("Canine");
    }
    @Override
    void eat() {
        System.out.println("I eat kibble!");
    }
    @Override
    void move() {
        System.out.println("Running and rolling and wagging!");
    }
    //...
}

Les interfaces peuvent être utilisées dans une variété de classes peut importe leur lien de parenté :

1
2
3
4
5
6
7
8
public class Car implements Moving {
    //...
    @Override
    void move() {
        System.out.println("Driving!");
    }
    //...
}

L’implémentation d’une interface signale une relation “a un” entre la classe et l’interface. La classe Car a une méthode move() qui est définie dans l’interface Moving, mais on ne dirait pas que Car est un Moving parce qu’il peut être d’autres choses aussi, notamment une autre classe parente (p. ex. Vehicle).

Voici le diagramme de classe UML simplifié pour les relations d’implémentation dans ces exemples.

interface

Notez que la flèche est différente pour l’implémentation : la tête est pleine mais la ligne est pointillée.

Le but des interfaces et de fournir une interface de programmation (API) pour toutes les classes qui implémentent une fonctionnalité commune. Par exemple, dans la bibliothèque standard de Java toutes les collections - comme les listes, les ensembles et les dictionnaires - implémentent l’interface Collection qui définit les méthodes add(), remove(), contains(), etc. Ainsi toutes les classes peuvent implémenter à leur façon ces méthodes, mais les utilisateurs de ces classes accèdent à la fonctionnalité avec les mêmes appels de méthodes. Cela rend les grands projets plus prévisibles, extensibles et faciles à utiliser.

Objectifs d’apprentissage

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

Critères de succès

Exercices

Quiz de consolidation

Travail pratique:

  1. Pour une catégorie d’objets nommée GuiWindow (pour les fenêtres qui s’ouvrent sur un ordinateur), pensez à une courte liste (soit une liste partielle) d’interfaces qui pourraient être implémentées par les classes de cette catégorie. Idées : on peut fermer, redimensionner, réduire, etc. les fenêtres. Pour la classe GuiWindow et les interfaces indentifiées, créez les déclarations de base dans des fichiers Java. L’utilité de chaque interface devrait être évidente par son nom et par le nom de sa ou ses méthodes abstraites.
  2. Créez le diagramme de classe UML simplifié (sans contenu interne) pour montrer les relations entre les classes et les interfaces que vous avez déclarées dans la question précédente.