Participer au site avec un Tip
Rechercher
 

Améliorations / Corrections

Vous avez des améliorations (ou des corrections) à proposer pour ce document : je vous remerçie par avance de m'en faire part, cela m'aide à améliorer le site.

Emplacement :

Description des améliorations :

Les classes scellées (Sealed Classes - Java SE 17)

Aspects avancés sur la définition d'interfaces Introduction à la gestion des exceptions


Accès rapide :
La vidéo
Qu'est-ce qu'une classe scellée ?
Définition d'une classe scellée
Définir une classe dérivée d'une classe scellée
Définir une sous-classe finale
Définir une sous-classe ouverte
Définir une sous-classe elle-même scellée
Vous pouvez sceller d'autres types de données

le concept de classes scellées est relativement récent dans l'histoire du langage Java. Il est apparu à partir de la version 17 du Java SE (septembre 2021). Ce concept était aussi présent dans le Java SE 15 et 16 en « Preview Features » (il fallait activer l'option --enable-preview au démarrage de la JVM pour accéder à ces fonctionnalités). Si vous utiliser une version antérieure de Java, les codes proposés ne pourront pas fonctionner : je vous recommande alors de télécharger une version moderne de Java.

La vidéo

Cette vidéo vous montre comment définir des classes scellées en Java, disponible depuis le Java SE 17. Une classe scellée permet de restreindre les possibilités d'héritage. Le tuto montre aussi comment définir des interfaces scellées.


Les classes scellées (Sealed Classes - Java SE 17)

Qu'est-ce qu'une classe scellée ?

Dans les chapitres précédents, nous avons vu qu'il est possible d'étendre, par héritage, une classe existante pour enrichir son comportement. Mais dans certaines situations et pour des raisons diverses (performances...), on ne souhaite ne pas permettre n'importe quoi en termes d'héritage. Nous avons vu qu'il était possible d'interdire l'héritage d'une classe grâce au mot clé final et ce depuis la première version de Java.

Depuis le Java SE 17, il est maintenant possible de restreindre les possibilités d'héritage, sans complétement les interdire : pour ce faire, on peut définir des « classes scellées ». Une classe scellée indique quels sont les types de données autorisés à dériver de cette classe.

cette nouvelle possibilité a nécessité l'ajout de nouveaux mots clés contextuels dans le langage. Il y en a trois : scealed, permits et non-sealed. Ces mots clés sont dits contextuels, car si vous les utilisez comme noms de variables ou noms de méthodes, cela fonctionne très bien : effectivement avant l'apparition des classes scellées, ces mots n'étaient pas réservés et peut-être en aviez-vous déjà utilisé un. Si tel est le cas, votre ancien programme continuera à compiler et à s'exécuter.

Définition d'une classe scellée

Pour définir une classe scellée, on utilise le mot clé contextuel sealed. On complète la définition via le mot clé contextuel permits pour indiquer quels sont les types autorisés à dériver de la classe scellée. Voici un exemple d'utilisation.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
package fr.koor.samples;

public sealed class Shape permits Circle, Square {
    
    private Point center;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de définition d'une classe scellée

Dans cet exemple, on part du principe qu'on ne désire pas autoriser la présence d'autres classes de figures géométriques. Seuls des cercles et des carrés seront autorisés. Voici comment définir les classes Circle et Square.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public final class Circle extends Shape {
    
    private double radius;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation d'une classe scellée
faites de même pour une classe Square.

Si vous tentez, tout de même, d'hériter de la classe scellée, voici le message d'erreur qui sera produit par votre compilateur.

 1 
 2 
 3 
 4 
 5 
public final class Triangle extends Shape {
    
    // Imaginez des attributs et des méthodes.
    
}
Exemple de dérivation, non permise, d'une classe scellée

On lance manuellement la compilation de la classe non-autorisée.

$> javac Triangle.java
Shape.java:1: error: class is not allowed to extend sealed class: Shape (as it is not listed in its permits clause)
public final class Triangle extends Shape {
             ^
1 error

Définir une classe dérivée d'une classe scellée

Lorsque vous allez tenter de dériver une classe d'une classe parente scellée, trois alternatives vous sont proposées.

si vous n'utilisez pas l'une de ces trois solutions, une erreur de compilation sera produire.
 1 
 2 
 3 
 4 
 5 
 6 
 7 
public class Square extends Shape {
    
    private double length;
    
    // Imaginez d'autres attributs et méthodes.
    
}
On ne choisit pas l'une des trois stratégies supportées : ca se passera mal.

Et voici le message d'erreur produit par le compilateur :

Shape.java:1: error: sealed, non-sealed or final modifiers expected
public class Square extends Shape {
       ^
1 error

Définir une sous-classe finale

C'est très exactement ce que nous avons fait dans les exemples précédents. Si vous scellez une classe, c'est pour limiter le nombre de classes qui vont en dériver (directement ou indirectement). Le fait de finaliser la classe fille interdira de créer une éventuelle sous-classe ColoredCircle qui dériverait de la classe Circle.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public final class Circle extends Shape {
    
    private double radius;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation d'une classe scellée par une classe finale

Définir une sous-classe ouverte

En faisant ce choix, vous indiquez au compilateur que toutes les sous-classes de la classes ouverte seront acceptées. On introduit ce type de classe via le mot clé contextuel non-sealed.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public non-sealed class Circle extends Shape {
    
    private double radius;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation d'une classe scellée par une classe ouverte

Vous pouvez maintenant envisager de définir la sous-classe ColoredCircle.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public class ColoredCircle extends Circle {
    
    private Color color;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation d'une classe ColoredCircle

Définir une sous-classe elle-même scellée

Enfin, on peut aussi sceller la sous-classe : dans ce cas, elle devra à son tour lister les sous-classes autorisées à en dériver.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public sealed class Circle extends Shape permits ColoredCircle {
    
    private double radius;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation d'une classe scellée par une classe ouverte

Dans ce cas, vous devrez de nouveau choisir une des trois stratégies pour la définition de la classe ColoredCircle : pour ma part, j'ai choisi la stratégie « classe finale ».

 1 
 2 
 3 
 4 
 5 
 6 
 7 
public final class ColoredCircle extends Circle {
    
    private Color color;
    
    // Imaginez d'autres attributs et méthodes.
    
}
Exemple de dérivation de la classe finale ColoredCircle

Vous pouvez sceller d'autres types de données

Il est aussi possible de sceller des classes abstraites ou des interfaces. Voici un exemple vous montrant comment sceller une interface, cette interface étant ensuite implémentée par une classe de type « record » (pourquoi pas). Voici le code de l'interface scellée.

 1 
 2 
 3 
 4 
 5 
 6 
public sealed interface Shape permits Circle, Square {
    
    double area();
    double perimeter();
    
}
Exemple d'une interface scellée.

Voici maintenant la définition de notre premier record Circle.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
public record Circle (Point center, double radius) implements Shape {

    @Override
    public double area() {
        return Math.PI * radius() * radius();
    }
    
    @Override
    public double perimeter() {
        return 2 * Math.PI * radius();
    }
    
}
Exemple d'un record implémentant une interface scellée.
une classe de type record est implicitement finale. C'est pour cela qu'aucune stratégie apparente n'y est précisé.

Et voici le code de notre second type record.

 1 
 2 
 3 
 4 
 5 
 6 
 7 
 8 
 9 
 10 
 11 
 12 
 13 
record Square (Point center, double length) implements Shape {

    @Override
    public double area() {
        return length() * length();
    }
    
    @Override
    public double perimeter() {
        return 4 * length();
    }
    
}
Exemple d'un record implémentant une interface scellée.


Aspects avancés sur la définition d'interfaces Introduction à la gestion des exceptions