Classes abstraites en Java

10 Sep 2019 10 Sep 2019 7789 vues ESSADDOUKI Mostafa 12 min de lecture
Introduction
1 Nouveautés de Java 11 2 Différences entre JDK, JRE et JVM 3 Structure d'un programme Java - Hello World 4 Mots clés et conventions de dénomination en Java 5 Types de données intégrés en Java 6 Les variables en Java 7 Classes enveloppe - Number, Integer, Double ... 8 Lire les entrées clavier en Java
Structures de contrôle
9 Les opérateurs en Java 10 Les structures conditionnelles en Java 11 Les boucles en Java 12 Instructions de contrôle de boucle - break, continue
Chaines de caractères
13 Les chaines en Java - API String 14 Les chaines en Java - StringBuffer et StringBuilder 15 Les expressions régulières en Java
Programmation OO
16 Objets et classes en Java 17 Modificateurs d'accès Java - public, private, protected et package 18 Méthodes et surcharge des méthodes en Java 19 les constructeurs en Java 20 L'héritage en Java 21 Classes abstraites en Java 22 Interfaces et héritage multiple en Java 23 Les classes imbriquées en Java 24 Les singletons en Java 25 Classes et méthodes génériques 26 Interface fonctionnelle et expressions Lambda en Java
Tableaux et collections
27 Les tableaux en Java 28 Classe Arrays - java.util.Arrays 29 Les listes dynamiques - java.util.ArrayList 30 Les listes chaînées en Java - java.util.LinkedList 31 HashSet en Java - java.util.HashSet 32 HashMap en Java - java.util.HashMap
Gestion des fichiers
33 Comprendre les fichiers informatiques 34 Utilisation des classes Path et Files en Java 35 Lecture et écriture dans un fichier en Java 36 Fichiers à accès aléatoire en Java
Gestion d'exceptions
37 Gestion d'exceptions en Java 38 Créez vos propres classes d'exception en Java
Programmation concurrente
39 Introduction à la programmation concurrente en Java - Multi-threads 40 classe java.lang.Thread 41 Synchronisation des threads en Java
Cours Java pour les débutants — Étape 21 sur 41

Classes et méthodes abstraites en Java

  Prérequis

Maîtriser les classes, les objets, l'héritage et les modificateurs d'accès. Comprendre le mot-clé extends et le constructeur super(). Connaître la notion de redéfinition de méthode (@Override).

  Objectifs

Distinguer classe concrète et classe abstraite. Déclarer des classes et méthodes abstraites avec le mot-clé abstract. Comprendre les règles d'implémentation dans les sous-classes. Utiliser les classes abstraites pour modéliser des hiérarchies cohérentes.

1. Classe concrète vs classe abstraite

Classe concrète Une classe concrète est une classe dont on peut instancier des objets directement avec new. Toutes ses méthodes ont une implémentation complète.
Classe abstraite Une classe abstraite est une classe trop générale pour être instanciée directement. Elle sert uniquement de modèle pour l'héritage. Elle est déclarée avec le mot-clé abstract et peut contenir des méthodes abstraites (sans corps) et des méthodes concrètes (avec corps).

  Exemple — Instanciation interdite

abstract class Forme {
    abstract double surface();
}

Forme f = new Forme(); // Erreur : Forme is abstract; cannot be instantiated
InstantiationError Tenter d'instancier directement une classe abstraite provoque une erreur de compilation. Une classe abstraite ne peut être utilisée qu'à travers ses sous-classes concrètes.

2. Déclarer une classe et des méthodes abstraites

   
Syntaxe — Classe et méthode abstraites Java
public abstract class NomClasse {

    // Méthode abstraite — sans corps, se termine par ;
    public abstract typeRetour nomMethode(paramètres);

    // Méthode concrète — avec corps, héritée telle quelle
    public typeRetour autreMethode() {
        // implémentation
    }
}
Règles fondamentales
  • Une méthode abstraite n'a pas de corps — sa déclaration se termine par ;.
  • Si une classe contient au moins une méthode abstraite, elle doit être déclarée abstract.
  • Une classe abstraite peut contenir des méthodes concrètes, des attributs, des constructeurs et des méthodes static.
  • Une sous-classe concrète doit implémenter toutes les méthodes abstraites héritées, sinon elle doit elle-même être déclarée abstract.
Élément Classe abstraite Classe concrète
Instanciable (new) Non Oui
Méthodes abstraites Autorisées Interdites
Méthodes concrètes Autorisées Autorisées
Constructeur Oui (appelé par super()) Oui
Peut être étendue Oui Oui (si non final)

3. Exemple complet — Hiérarchie de formes géométriques

Le type de base est Forme — chaque forme a une couleur et un comportement commun (getCouleur(), afficher()). La méthode surface() est abstraite car chaque forme la calcule différemment. Les classes Cercle et Rectangle étendent Forme et fournissent leur propre implémentation.

  Exemple 1 — Hiérarchie complète (Java 21)

abstract class Forme {

    private String couleur;

    public Forme(String couleur) {
        System.out.println("Constructeur Forme");
        this.couleur = couleur;
    }

    public abstract double surface();

    public abstract double perimetre();

    public String getCouleur() {
        return couleur;
    }

    public void afficher() {
        System.out.printf("Forme : %-12s | Couleur : %-6s | Surface : %8.4f | Périmètre : %8.4f%n",
            getClass().getSimpleName(), couleur, surface(), perimetre());
    }
}

class Cercle extends Forme {

    private double rayon;

    public Cercle(String couleur, double rayon) {
        super(couleur);
        System.out.println("Constructeur Cercle");
        this.rayon = rayon;
    }

    @Override
    public double surface() {
        return Math.PI * rayon * rayon;
    }

    @Override
    public double perimetre() {
        return 2 * Math.PI * rayon;
    }
}

class Rectangle extends Forme {

    private double hauteur;
    private double largeur;

    public Rectangle(String couleur, double hauteur, double largeur) {
        super(couleur);
        System.out.println("Constructeur Rectangle");
        this.hauteur = hauteur;
        this.largeur = largeur;
    }

    @Override
    public double surface() {
        return hauteur * largeur;
    }

    @Override
    public double perimetre() {
        return 2 * (hauteur + largeur);
    }
}

class Triangle extends Forme {

    private double a, b, c;

    public Triangle(String couleur, double a, double b, double c) {
        super(couleur);
        System.out.println("Constructeur Triangle");
        this.a = a;
        this.b = b;
        this.c = c;
    }

    @Override
    public double surface() {
        double s = perimetre() / 2;
        return Math.sqrt(s * (s - a) * (s - b) * (s - c));
    }

    @Override
    public double perimetre() {
        return a + b + c;
    }
}

public class Test {
    public static void main(String[] args) {
        Forme f1 = new Cercle("Rouge", 2.2);
        Forme f2 = new Rectangle("Vert", 2, 4);
        Forme f3 = new Triangle("Bleu", 3, 4, 5);

        System.out.println("\n=== Résultats ===");
        f1.afficher();
        f2.afficher();
        f3.afficher();
    }
}
Sortie
Constructeur Forme
Constructeur Cercle
Constructeur Forme
Constructeur Rectangle
Constructeur Forme
Constructeur Triangle

=== Résultats ===
Forme : Cercle       | Couleur : Rouge  | Surface :  15.2053 | Périmètre :  13.8230
Forme : Rectangle    | Couleur : Vert   | Surface :   8.0000 | Périmètre :  12.0000
Forme : Triangle     | Couleur : Bleu   | Surface :   6.0000 | Périmètre :  12.0000
getClass().getSimpleName() Cette méthode héritée de Object retourne le nom simple de la classe réelle de l'objet — ici "Cercle", "Rectangle" ou "Triangle" selon l'instance. C'est le polymorphisme en action.

4. Polymorphisme avec les classes abstraites

Polymorphisme avec classe abstraite Une variable de type classe abstraite peut référencer n'importe quelle instance d'une sous-classe concrète. Lors de l'appel d'une méthode abstraite, c'est l'implémentation de la classe réelle de l'objet qui est exécutée. C'est le polymorphisme dynamique (résolution à l'exécution).

  Exemple 2 — Traitement polymorphe d'un tableau de formes

Forme[] formes = {
    new Cercle("Rouge", 5.0),
    new Rectangle("Bleu", 3.0, 7.0),
    new Triangle("Vert", 6.0, 8.0, 10.0),
    new Cercle("Jaune", 2.5)
};

double surfaceTotale = 0;
for (Forme f : formes) {
    surfaceTotale += f.surface();
    f.afficher();
}

System.out.printf("%nSurface totale : %.4f%n", surfaceTotale);
Sortie (constructeurs omis pour la lisibilité)
Forme : Cercle       | Couleur : Rouge  | Surface :  78.5398 | Périmètre :  31.4159
Forme : Rectangle    | Couleur : Bleu   | Surface :  21.0000 | Périmètre :  20.0000
Forme : Triangle     | Couleur : Vert   | Surface :  24.0000 | Périmètre :  24.0000
Forme : Cercle       | Couleur : Jaune  | Surface :  19.6350 | Périmètre :  15.7080

Surface totale : 143.1748

5. Méthodes concrètes dans une classe abstraite

Une classe abstraite peut contenir des méthodes concrètes (avec implémentation). Ces méthodes sont héritées par toutes les sous-classes sans devoir être redéfinies, ce qui évite la duplication de code.

  Exemple 3 — Méthode concrète partagée

abstract class Animal {

    private String nom;
    private String espece;

    public Animal(String nom, String espece) {
        this.nom    = nom;
        this.espece = espece;
    }

    public abstract String cri();

    public void sePresenter() {
        System.out.println("Je m'appelle " + nom + ", je suis un(e) " + espece
                           + " et je fais : " + cri());
    }

    public String getNom()    { return nom; }
    public String getEspece() { return espece; }
}

class Chien extends Animal {
    public Chien(String nom) {
        super(nom, "Chien");
    }

    @Override
    public String cri() { return "Ouaf !"; }
}

class Chat extends Animal {
    public Chat(String nom) {
        super(nom, "Chat");
    }

    @Override
    public String cri() { return "Miaou !"; }
}

class Vache extends Animal {
    public Vache(String nom) {
        super(nom, "Vache");
    }

    @Override
    public String cri() { return "Meuh !"; }
}

public class TestAnimal {
    public static void main(String[] args) {
        Animal[] animaux = {
            new Chien("Rex"),
            new Chat("Minou"),
            new Vache("Marguerite")
        };
        for (Animal a : animaux) {
            a.sePresenter();
        }
    }
}
Sortie
Je m'appelle Rex, je suis un(e) Chien et je fais : Ouaf !
Je m'appelle Minou, je suis un(e) Chat et je fais : Miaou !
Je m'appelle Marguerite, je suis un(e) Vache et je fais : Meuh !
Avantage des méthodes concrètes dans une classe abstraite La méthode sePresenter() est définie une seule fois dans Animal. Elle appelle cri() qui est abstraite — c'est l'implémentation de la classe réelle qui est invoquée à l'exécution. Sans les classes abstraites, il faudrait dupliquer sePresenter() dans chaque sous-classe.

6. Sous-classe abstraite

Une sous-classe d'une classe abstraite peut elle-même rester abstraite si elle n'implémente pas toutes les méthodes abstraites héritées. C'est utile pour créer des hiérarchies à plusieurs niveaux.

  Exemple 4 — Hiérarchie abstraite à plusieurs niveaux

abstract class FormeColoree extends Forme {

    private double opacite;

    public FormeColoree(String couleur, double opacite) {
        super(couleur);
        this.opacite = opacite;
    }

    public double getOpacite() { return opacite; }

    public abstract String getDescription();
}

class CercleColore extends FormeColoree {

    private double rayon;

    public CercleColore(String couleur, double opacite, double rayon) {
        super(couleur, opacite);
        this.rayon = rayon;
    }

    @Override public double surface()       { return Math.PI * rayon * rayon; }
    @Override public double perimetre()     { return 2 * Math.PI * rayon; }
    @Override public String getDescription() {
        return "Cercle coloré de rayon " + rayon + " (opacité : " + getOpacite() + ")";
    }
}

7. Pattern Matching avec les classes abstraites (Java 21)

  Complément — switch et Pattern Matching (Java 21)

Java 21 permet d'utiliser le pattern matching dans un switch pour traiter différentes sous-classes d'une classe abstraite de façon concise et sûre.

static String decrire(Forme f) {
    return switch (f) {
        case Cercle c    -> String.format("Cercle de rayon %.2f (surface : %.4f)", c.rayon, c.surface());
        case Rectangle r -> String.format("Rectangle %sx%s (surface : %.4f)", r.hauteur, r.largeur, r.surface());
        case Triangle t  -> String.format("Triangle (surface : %.4f)", t.surface());
        default          -> "Forme inconnue";
    };
}

public static void main(String[] args) {
    Forme[] formes = { new Cercle("Rouge", 3.0), new Rectangle("Bleu", 4.0, 5.0) };
    for (Forme f : formes) {
        System.out.println(decrire(f));
    }
}
Sortie
Cercle de rayon 3.00 (surface : 28.2743)
Rectangle 4.0x5.0 (surface : 20.0000)

8. Exercice

Hiérarchie de véhicules

Niveau : Intermédiaire

Modéliser une hiérarchie de véhicules en utilisant les classes et méthodes abstraites.

Travail demandé

  1. Créer une classe abstraite Vehicule avec :
    • Attributs : marque (String), vitesseMax (double).
    • Méthode abstraite : double consommation(double distanceKm) — consommation en litres pour une distance donnée.
    • Méthode concrète : void afficher(double distanceKm) — affiche la marque, la vitesse max et la consommation calculée.
  2. Créer deux sous-classes concrètes :
    • Voiture(marque, vitesseMax, litresPar100km) — consommation = distance * litresPar100km / 100
    • Moto(marque, vitesseMax, litresPar100km) — consommation = distance * litresPar100km / 100 mais 20% de moins qu'une voiture à distance égale
  3. Tester dans un main avec une distance de 250 km.
Sortie attendue
Sortie
Marque : Renault    | Vmax : 180.0 km/h | Conso (250 km) : 17.50 L
Marque : Yamaha     | Vmax : 220.0 km/h | Conso (250 km) :  8.00 L

  L'essentiel en bref

Une classe abstraite (mot-clé abstract) est trop générale pour être instanciée directement — elle sert uniquement de modèle pour l'héritage. Elle peut contenir des méthodes abstraites (sans corps, terminées par ;) que chaque sous-classe concrète doit impérativement implémenter, et des méthodes concrètes héritées telles quelles pour éviter la duplication de code. Toute classe contenant au moins une méthode abstraite doit être déclarée abstract. Une sous-classe qui n'implémente pas toutes les méthodes abstraites héritées doit elle-même rester abstract. Les classes abstraites constituent la base du polymorphisme dynamique : une variable du type abstrait peut référencer n'importe quelle instance d'une sous-classe, et c'est l'implémentation de la classe réelle qui est invoquée à l'exécution.

Sortie
// La sortie apparaîtra ici…
Prêt · Ctrl+Entrée pour exécuter

Discussion (0)

Soyez le premier à laisser un commentaire !

Laisser un commentaire

Votre commentaire sera visible après modération.