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, toute autre classe peut l’étendre (sauf si vous la marquez final). Mais que faire si vous souhaitez autoriser l’héritage, mais pas de la part de n’importe qui ? Java 17 a introduit les classes scellées 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 abstract 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 qui tente de l’étendre provoquera une erreur de compilation.
Chaque sous-classe autorisée doit utiliser l’un des trois modificateurs suivants : final (aucune extension supplémentaire), sealed (poursuit la chaîne de restriction) ou non-sealed (permet une extension sans restriction) :
sealed class Vehicle permits Car, Truck { }
final class Car extends Vehicle { } // Ne peut pas être étendu
non-sealed class Truck extends Vehicle { } // N'importe qui peut étendre TruckLes classes scellées sont particulièrement puissantes avec la correspondance de motifs 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 modes de paiement, les types de réponse ou les formes géométriques.
Défi
FacileConstruisons un système de notifications qui illustre les classes scellées en limitant les types de notifications pouvant exister dans votre application. Vous créerez une hiérarchie où seuls certains types de notifications sont autorisés, et où chaque type gère la livraison différemment.
Vous organiserez votre code sur quatre fichiers :
Notification.java: créez une classe abstraite scellée qui sert de base à toutes les notifications. Elle doit autoriser exactement trois sous-classes :EmailNotification,SMSNotificationetPushNotification. Votre classe scellée doit posséder un champ protégémessage(String), un constructeur pour l'initialiser, un getter pour le message, ainsi qu'une méthode abstraitedeliver()qui renvoie une String décrivant comment la notification est envoyée.EmailNotification.java: créez une classefinalqui étend Notification. Une EmailNotification possède un champ privé supplémentairerecipient(String) correspondant à l'adresse e-mail. Son constructeur doit recevoir le message et le destinataire, en appelant le constructeur parent avecsuper(). La méthodedeliver()doit renvoyer :Sending email to [recipient]: [message]SMSNotification.java: créez une classenon-sealedqui étend Notification. Cela permet à d'autres classes de l'étendre si nécessaire à l'avenir. Une SMSNotification possède un champ privé supplémentairephoneNumber(String). Son constructeur reçoit le message et le numéro de téléphone. La méthodedeliver()doit renvoyer :Sending SMS to [phoneNumber]: [message]PushNotification.java: créez une classefinalqui étend Notification. Une PushNotification possède un champ privé supplémentairedeviceId(String). Son constructeur reçoit le message et l'identifiant de l'appareil. La méthodedeliver()doit renvoyer :Sending push to device [deviceId]: [message]Main.java: assemblez votre système de notifications ! 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 notification de chaque type en utilisant le même message, mais leurs coordonnées respectives. Stockez les trois notifications dans un tableau de type
Notification[]afin de démontrer le polymorphisme avec les classes scellées. Parcourez ensuite le tableau et affichez le résultat de l'appel àdeliver()sur chaque notification.
Vous recevrez quatre entrées dans l'ordre suivant : 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 !
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 une 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[]
// Ceci démontre le polymorphisme avec les classes scellées
// TODO: Parcourir le tableau et afficher le résultat de deliver()
// pour chaque notification
}
}
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
1Fondamentaux de la POO
Fichiers externesIntroduction à la POOClasses vs ObjetsLe mot-clé thisMéthodesChamps (Attributs)Méthode constructeurSurcharge de constructeurRécapitulatif - Calculatrice simple4Héritage
Héritage de base (extends)Le mot-clé superRedéfinition de méthode (@Override)Chaînage de constructeursLa classe ObjectHéritage simple et multiniveauPourquoi pas d'héritage multiple de classesRécapitulatif - Hiérarchie des employés7Méthodes spéciales et classe Object
Méthode toString()equals() et hashCode()Méthode clone()compareTo() et ComparableInterface ComparatorRécapitulatif - Tri personnalisé2Modificateurs d'accès et Encapsulation
Aperçu des niveaux d'accèsMéthodes Getter et SetterMasquage d'informationsLe mot-clé finalRécapitulatif - Gestionnaire de compte bancaire5Polymorphisme
Bases de la surcharge de méthodesRedéfinition de méthodes (Run-Time)Upcasting et DowncastingL'opérateur instanceofClasses et méthodes abstraitesRécapitulatif - Calculateur de formes8Concepts avancés de la POO
Composition vs HéritageAgrégation vs CompositionClasses internes, imbriquées et anonymesEnums et méthodes d'EnumRecords (Java 16+)Classes scellées (Java 17+)11Patrons de conception, partie 1
Introduction aux patrons de conceptionPatron SingletonPatron FabriquePatron MonteurPatron ObservateurPatron Stratégie3Propriétés de classe et membres statiques
Variables d'instance vs variables statiquesMéthodes statiquesBlocs statiquesConstantes (static final)Récapitulatif - Compteur et utilitaire6Interfaces et Classes Abstraites
Introduction aux InterfacesImplémentation d'InterfacesImplémentation d'Interfaces MultiplesDefault et Static dans les InterfacesClasses Abstraites vs InterfacesInterfaces FonctionnellesRécapitulatif - Système de Paiement9La généricité
Introduction à la généricitéClasses génériquesMéthodes génériquesParamètres de type bornésWildcards (?, extends, super)Récapitulatif - Conteneur générique12Patrons de conception, partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Template MethodPatron ÉtatPatron CompositePatron ItérateurEntraînez-vous par vous-même : Compilateur Java en ligne