Menu
Coddy logo textTech

Classes scellées (Java 17+)

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

En Java traditionnel, lorsque vous créez une classe, n'importe quelle autre classe peut l'étendre (à moins que vous ne la marquiez comme final). Mais que se passe-t-il si vous voulez autoriser l'héritage, mais pas de la part de n'importe qui ? Java 17 a introduit les classes scellées (sealed classes) pour vous donner un contrôle précis sur les classes qui peuvent étendre la vôtre.

Une classe scellée déclare explicitement ses sous-classes autorisées à l'aide des mots-clés sealed et permits :

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

Seuls Circle, Rectangle et Triangle peuvent étendre Shape. Toute autre classe tentant de l'étendre provoquera une erreur de compilation.

Chaque sous-classe autorisée doit utiliser l'un des trois modificateurs : final (pas d'extension supplémentaire), sealed (poursuit la chaîne de restriction) ou non-sealed (ouvre à une extension sans restriction) :

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Ne peut pas être étendue
non-sealed class Truck extends Vehicle { }    // N'importe qui peut étendre Truck

Les classes scellées sont particulièrement puissantes avec le filtrage par motif (pattern matching) dans les expressions switch, car le compilateur connaît tous les sous-types possibles et peut vérifier l'exhaustivité. Elles sont idéales pour modéliser un ensemble fixe de types dans un domaine, comme les méthodes de paiement, les types de réponse ou les formes géométriques.

challenge icon

Défi

Facile

Construisons un système de notification qui démontre l'utilisation des classes scellées (sealed classes) en restreignant les types de notifications pouvant exister dans votre application. Vous allez créer une hiérarchie où seuls des types de notification spécifiques sont autorisés, et chaque type gère la distribution différemment.

Vous organiserez votre code sur quatre fichiers :

  • Notification.java : Créez une classe abstraite scellée (sealed abstract class) qui sert de base à toutes les notifications. Elle doit autoriser exactement trois sous-classes : EmailNotification, SMSNotification, et PushNotification. Votre classe scellée a besoin d'un champ protégé message (String), d'un constructeur pour l'initialiser, d'un getter pour le message, et d'une méthode abstraite deliver() qui retourne une String décrivant comment la notification est envoyée.
  • EmailNotification.java : Créez une classe final qui étend Notification. Une EmailNotification possède un champ privé supplémentaire recipient (String) pour l'adresse e-mail. Son constructeur doit prendre à la fois le message et le destinataire, en appelant le constructeur parent avec super(). La méthode deliver() doit retourner : "Sending email to " + recipient + ": " + message;
  • SMSNotification.java : Créez une classe non-sealed qui étend Notification—cela permet à d'autres classes de l'étendre si nécessaire à l'avenir. Une SMSNotification possède un champ privé supplémentaire phoneNumber (String). Son constructeur prend le message et le numéro de téléphone. La méthode deliver() doit retourner : "Sending SMS to " + phoneNumber + ": " + message;
  • PushNotification.java : Créez une classe final qui étend Notification. Une PushNotification possède un champ privé supplémentaire deviceId (String). Son constructeur prend le message et l'identifiant de l'appareil. La méthode deliver() doit retourner : "Sending push to device " + deviceId + ": " + message;
  • Main.java : Rassemblez votre système de notification ! Vous recevrez quatre entrées : un message (String), une adresse e-mail (String), un numéro de téléphone (String), et un identifiant d'appareil (String).

    Créez une instance de chaque type de notification en utilisant le même message mais leurs informations de contact respectives. Stockez les trois dans un tableau de type Notification[] pour démontrer le polymorphisme avec les classes scellées. Ensuite, parcourez le tableau et affichez le résultat de l'appel à deliver() sur chaque notification.

Vous recevrez quatre entrées dans l'ordre : message, adresse e-mail, numéro de téléphone, et identifiant d'appareil.

Remarquez comment la classe scellée restreint la hiérarchie—seules les trois classes autorisées peuvent étendre Notification. Chaque sous-classe doit se déclarer comme final, sealed, ou non-sealed. Cela vous donne un contrôle total sur votre hiérarchie de types tout en permettant le polymorphisme !

Aide-mémoire

Une classe scellée (sealed class) restreint les classes qui peuvent l'étendre en utilisant les mots-clés sealed et permits :

sealed class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

Seules les classes listées dans permits peuvent étendre la classe scellée. Toute autre classe tentant de l'étendre provoquera une erreur de compilation.

Chaque sous-classe autorisée doit utiliser l'un des trois modificateurs suivants :

  • final - empêche toute extension ultérieure
  • sealed - continue la chaîne de restriction avec ses propres sous-classes autorisées
  • non-sealed - ouvre la hiérarchie pour une extension sans restriction
sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // Ne peut pas être étendue
non-sealed class Truck extends Vehicle { }    // N'importe qui peut étendre Truck

Les classes scellées sont utiles pour modéliser un ensemble fixe de types dans un domaine, tels que les méthodes de paiement, les types de réponse ou les formes géométriques. Elles fonctionnent bien avec le filtrage par motif (pattern matching) dans les expressions switch, car le compilateur connaît tous les sous-types possibles.

Essayez vous-même

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lire les quatre entrées
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Créer un exemplaire de chaque type de notification en utilisant le même message
        // - EmailNotification avec message et email
        // - SMSNotification avec message et phoneNumber
        // - PushNotification avec message et deviceId
        
        // TODO: Stocker les trois notifications dans un tableau Notification[]
        // Cela démontre le polymorphisme avec les sealed classes
        
        // TODO: Parcourir le tableau et afficher le résultat de deliver()
        // pour chaque notification
        
    }
}
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