Menu
Coddy logo textTech

Classes Abstraites vs Interfaces

Fait partie de la section Programmation Orientée Objet du Journey Java de Coddy — leçon 38 sur 87.

Maintenant que vous comprenez à la fois les classes abstraites et les interfaces, une question courante se pose : quand devriez-vous utiliser l'une plutôt que l'autre ? Le choix dépend de ce que vous essayez de modéliser.

Utilisez une classe abstraite lorsque des classes partagent une base commune avec un état ou un comportement partagé. Les classes abstraites peuvent avoir des variables d'instance, des constructeurs et un mélange de méthodes abstraites et concrètes :

public abstract class Animal {
    protected String name;  // état partagé
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // comportement partagé
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // doit être implémenté
}

Utilisez une interface lorsque vous souhaitez définir une capacité que des classes non liées peuvent partager. Les interfaces se concentrent sur ce qu'un objet peut faire, et non sur ce qu'il est :

public interface Flyable {
    void fly();
}

// Des classes sans lien de parenté peuvent partager cette capacité
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }

Voici une comparaison rapide :

FonctionnalitéClasse abstraiteInterface
Variables d'instanceOuiUniquement des constantes
ConstructeursOuiNon
Héritage multipleNon (un seul extends)Oui (plusieurs implements)
Modificateurs d'accèsTousPublic uniquement (pour les méthodes abstraites)

Une règle pratique : si vous vous retrouvez à créer une abstract class avec seulement des méthodes abstraites et aucun état, une interface est probablement le meilleur choix.

challenge icon

Défi

Facile

Construisons un système de véhicules qui démontre quand utiliser des classes abstraites par rapport aux interfaces. Vous allez modéliser des véhicules qui partagent un état et un comportement communs via une classe abstraite, tout en ajoutant des capacités optionnelles via des interfaces.

Vous organiserez votre code sur cinq fichiers :

  • Vehicle.java : Créez une classe abstraite qui sert de fondation à tous les véhicules. Chaque véhicule possède un champ brand (String) et un champ year (int). Incluez un constructeur pour initialiser les deux champs, des méthodes getter pour chacun, et une méthode abstraite startEngine() qui retourne une String. Ajoutez également une méthode concrète getInfo() qui retourne : [brand] ([year]). C'est un cas d'utilisation parfait pour une classe abstraite — les véhicules partagent un état (brand, year) et certains comportements (getInfo), mais chacun démarre son moteur différemment.
  • Convertible.java : Définissez une interface pour les véhicules qui peuvent décapoter leur toit. Cette capacité n'est pas liée à ce qu'un véhicule est — c'est quelque chose que certains véhicules peuvent faire. Déclarez deux méthodes : openRoof() et closeRoof(), retournant toutes deux une String.
  • Car.java : Créez une classe qui étend Vehicle et implémente Convertible. Une Car possède un champ supplémentaire numDoors (int). Utilisez super pour initialiser les champs hérités. Implémentez startEngine() pour retourner : [brand] car engine started. Implémentez openRoof() pour retourner : [brand] roof opening et closeRoof() pour retourner : [brand] roof closing.
  • Motorcycle.java : Créez une classe qui étend Vehicle mais n'implémente PAS Convertible — les motos n'ont pas de toit ! Une Motorcycle possède un champ hasSidecar (boolean). Implémentez startEngine() pour retourner : [brand] motorcycle engine roaring.
  • Main.java : Rassemblez tout pour illustrer la différence entre les classes abstraites et les interfaces. Vous recevrez quatre entrées : une marque de voiture, une année de voiture, une marque de moto et une année de moto.

    Créez une Car (avec 4 portes) et une Motorcycle (sans side-car). Tout d'abord, démontrez le comportement partagé de la classe abstraite en affichant getInfo() pour les deux véhicules — ils héritent tous deux de Vehicle. Ensuite, affichez le résultat de startEngine() pour chacun — remarquez comment chaque type de véhicule implémente cela différemment.

    Enfin, démontrez la capacité de l'interface : puisque seule la Car implémente Convertible, appelez et affichez openRoof() suivi de closeRoof() sur la voiture uniquement.

Vous recevrez quatre entrées : la marque de la voiture (String), l'année de la voiture (int), la marque de la moto (String) et l'année de la moto (int).

Votre sortie devrait afficher six lignes au total. Remarquez comment les deux véhicules partagent l'état de la classe abstraite et la méthode getInfo(), mais seule la Car possède la capacité décapotable — cela illustre quand utiliser chaque approche !

Aide-mémoire

Utilisez une classe abstraite lorsque les classes partagent une base commune avec un état ou un comportement partagé. Les classes abstraites peuvent avoir des variables d'instance, des constructeurs et un mélange de méthodes abstraites et concrètes :

public abstract class Animal {
    protected String name;  // état partagé
    
    public Animal(String name) {
        this.name = name;
    }
    
    public void sleep() {  // comportement partagé
        System.out.println(name + " is sleeping");
    }
    
    public abstract void makeSound();  // doit être implémentée
}

Utilisez une interface lorsque vous souhaitez définir une capacité que des classes non liées peuvent partager. Les interfaces se concentrent sur ce qu'un objet peut faire, pas sur ce qu'il est :

public interface Flyable {
    void fly();
}

// Des classes non liées peuvent partager cette capacité
class Bird extends Animal implements Flyable { ... }
class Airplane implements Flyable { ... }
class Drone implements Flyable { ... }
CaractéristiqueClasse abstraiteInterface
Variables d'instanceOuiUniquement des constantes
ConstructeursOuiNon
Héritage multipleNon (extends unique)Oui (implements multiples)
Modificateurs d'accèsN'importe lequelPublic uniquement (pour les méthodes abstraites)

Ligne directrice : Si vous créez une classe abstraite avec seulement des méthodes abstraites et aucun état, une interface est probablement le meilleur choix.

Essayez vous-même

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lire les entrées
        String carBrand = scanner.nextLine();
        int carYear = scanner.nextInt();
        scanner.nextLine(); // consommer le saut de ligne
        String motorcycleBrand = scanner.nextLine();
        int motorcycleYear = scanner.nextInt();
        
        // TODO: Créer une Car avec 4 portes
        
        // TODO: Créer une Motorcycle sans sidecar (false)
        
        // TODO: Afficher getInfo() pour les deux véhicules (démontre le comportement partagé de la classe abstraite)
        
        // TODO: Afficher startEngine() pour les deux véhicules (démontre les différentes implémentations)
        
        // TODO: Afficher openRoof() et closeRoof() pour la voiture uniquement (démontre la capacité de l'interface)
    }
}
quiz iconTestez-vous

Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.

Toutes les leçons de Programmation Orientée Objet