Sealed Class — définition, principe de fonctionnement et application

Auteur : IT Sectr Publié le : 2026-05-26 Temps de lecture : 8 min

Sealed Class est un type spécial de classe en Kotlin qui restreint la hiérarchie d'héritage à un ensemble fixe de sous-types. Tous les sous-types sont déclarés dans le même fichier et sont connus du compilateur, ce qui permet d'utiliser un bloc when exhaustif sans branche else obligatoire. Selon Kotlin Docs, 2026, les classes scellées sont un mécanisme clé pour représenter des hiérarchies limitées, telles que les états, les types d'erreurs et les événements d'interface utilisateur.

Points essentiels

  • Sealed Class — classe avec un ensemble fixe de sous-types déclarés dans le même fichier.
  • Exhaustive when — le compilateur vérifie que tous les sous-types sont traités, éliminant les branches else oubliées.
  • Sealed interface — Kotlin 1.5+ prend en charge les interfaces scellées pour l'héritage multiple.
  • Hiérarchie d'erreurs — sealed class est la méthode standard de gestion d'erreurs type-safe en Kotlin.
  • Différence d'enum — chaque sous-type de sealed class peut contenir un état unique et un nombre différent de champs.

Qu'est-ce que Sealed Class ?

Sealed Class (classe scellée) est une classe en Kotlin marquée avec le modificateur sealed. Elle définit une hiérarchie de types restreinte : tous les sous-types possibles sont listés dans le même fichier et le compilateur connaît chacun d'eux. Cela distingue sealed class d'une classe ouverte ordinaire dont les sous-types peuvent être déclarés n'importe où.

L'objectif principal de sealed class est la représentation type-safe d'un ensemble fini de variantes. Chaque sous-type peut avoir sa propre structure de données, ce qui rend sealed class plus flexible que enum. À l'exécution, sealed class est une classe abstraite ordinaire ; le compilateur n'impose des restrictions qu'à la compilation.

Sealed class est particulièrement utile dans l'architecture d'applications Android : états d'interface utilisateur, résultats de requêtes réseau, événements de navigation de type Intent, et bien sûr les hiérarchies d'erreurs sont des cas d'utilisation typiques.

À la compilation, sealed class est optimisée en une table de saut pour les expressions when, ce qui la rend plus performante que les chaînes if-else. Combinée avec data class, chaque sous-type peut contenir non seulement un état mais aussi des méthodes, permettant de construire des modèles de domaine auto-documentés sans code passe-partout.

Sealed class est également efficace pour représenter des machines à états dans les applications mobiles. Chaque état est un sous-type séparé avec des paramètres uniques, et les transitions entre états sont contrôlées via des expressions when. Le compilateur garantit que tous les états possibles sont traités, éliminant les erreurs d'exécution lors du changement d'état de l'interface utilisateur ou de la logique métier.

Sealed Class vs Enum : différences clés

Les développeurs Kotlin débutants confondent souvent sealed class avec enum, car tous deux restreignent l'ensemble des valeurs. Cependant, il existe une différence fondamentale : enum est un ensemble de constantes du même type, tandis que sealed class est une hiérarchie de types différents.

Quand choisir enum

Enum est optimal lorsque toutes les variantes sont des constantes sans structure supplémentaire. Par exemple, les jours de la semaine, les statuts de commande ou les types d'actions sans paramètres. Chaque valeur d'enum est un singleton avec un nom fixe.

Quand choisir sealed class

Sealed class est nécessaire lorsque chaque variante a ses propres données. Par exemple, une erreur réseau contient un code de réponse, une erreur d'analyse contient des détails, et une erreur d'autorisation contient un message. Chaque sous-type de sealed class est un type séparé avec des champs uniques.

kotlin
// Enum — toutes les variantes d'un même type
enum class Status { LOADING, SUCCESS, ERROR }

// Sealed class — chaque variante avec ses propres données
sealed class UiState<out T> {
    object Loading : UiState<Nothing>()
    data class Success<T>(val data: T) : UiState<T>()
    data class Error(val message: String) : UiState<Nothing>()
}

Sealed Interface vs Sealed Class

Depuis Kotlin 1.5, il est possible de déclarer sealed interface. Cela étend le concept de scellement aux interfaces : une sealed interface a également un ensemble fixe d'implémentations mais prend en charge l'héritage multiple.

Quand utiliser sealed interface

Sealed interface est pratique lorsque les sous-types doivent implémenter plusieurs contrats simultanément. Par exemple, un événement d'interface utilisateur peut être à la fois cliquable et traçable. Avec sealed class, vous devriez choisir une classe de base ; avec sealed interface, le sous-type implémente les deux.

Limitations de sealed class

Sealed class est une classe, donc chaque sous-type ne peut avoir qu'un seul parent. Sealed interface résout ce problème mais ne peut pas contenir d'état. Le choix entre eux dépend de la tâche : si une logique partagée avec des champs est nécessaire — utilisez sealed class ; si la flexibilité des contrats est nécessaire — utilisez sealed interface.

kotlin
sealed interface ScreenEvent {
    data class Refresh(val force: Boolean) : ScreenEvent
    data class Navigate(val route: String) : ScreenEvent
    data class ShowError(val toast: String) : ScreenEvent
}

sealed interface AnalyticsEvent {
    val name: String
    val params: Map<String, Any>
}

// Le sous-type implémente les deux interfaces
data class LoginClicked(
    override val name: String = "login_click",
    override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent

Sealed Class pour les hiérarchies d'erreurs dans le développement mobile

L'une des principales utilisations de sealed class dans le développement mobile est la hiérarchie d'erreurs type-safe. Au lieu de lancer des exceptions de différents types ou d'utiliser une Exception générique, sealed class rassemble toutes les erreurs de domaine possibles en un seul type.

Comment construire une hiérarchie d'erreurs

Créez une sealed class DomainError et listez tous les types d'échec comme sous-types. Chaque sous-type contient uniquement les données pertinentes pour ce type d'erreur particulier. Le compilateur garantit que lors du traitement de l'erreur, vous n'oublierez aucune variante.

Exemple : gestion des erreurs d'authentification

Considérons une application avec autorisation où différents scénarios d'échec sont possibles : mot de passe incorrect, compte bloqué, problème serveur. Sealed class les combine en un seul type avec un traitement exhaustif.

kotlin
sealed class AuthError {
    data class InvalidCredentials(
        val attempts: Int
    ) : AuthError()

    data class AccountBlocked(
        val until: Long
    ) : AuthError()

    data class NetworkFailure(
        val cause: Throwable
    ) : AuthError()

    object ServerError : AuthError()
}

fun handleError(error: AuthError): String = when (error) {
    is AuthError.InvalidCredentials ->
        "Tentatives restantes : ${3 - error.attempts}"
    is AuthError.AccountBlocked ->
        "Accès bloqué jusqu'au ${Date(error.until)}"
    is AuthError.NetworkFailure ->
        "Vérifiez la connexion : ${error.cause.localizedMessage}"
    AuthError.ServerError ->
        "Serveur temporairement indisponible"
}

Patrons d'utilisation de Sealed Class dans Android

Sealed class est devenue un outil standard dans l'architecture des applications Android. Voyons trois patrons clés où sealed class est indispensable dans le développement mobile.

  • UI State — représentation de l'écran comme une machine à états : Loading, Content, Error. Chaque état contient ses propres données, et sealed class garantit que toutes les transitions sont traitées.
  • Navigation Event — sealed class au lieu de constantes de navigation : chaque écran est un sous-type séparé avec des paramètres de route. Le compilateur vérifie les types d'arguments.
  • Action/Intent — le patron Unidirectional Data Flow utilise sealed class pour représenter toutes les actions qu'un utilisateur peut effectuer sur un écran.

Il convient également de noter l'utilisation de sealed class dans Clean Architecture. Chaque couche (data, domain, presentation) utilise sealed class pour ses types d'erreur, et les mappers convertissent une sealed class en une autre. Par exemple, DataError de la couche data est mappé vers DomainError pour la logique métier, puis vers UiState pour la couche de présentation. Cela préserve la sécurité des types à tous les niveaux de l'application et garantit qu'aucune erreur ne reste non traitée.

Test des hiérarchies Sealed Class

Les tests de sealed class nécessitent une approche spéciale, car chaque sous-type est un type séparé avec son propre état. Il est recommandé d'écrire des tests paramétrés qui parcourent tous les sous-types de sealed class. Cela garantit que les expressions when couvrent toutes les variantes, y compris les nouvelles ajoutées lors de l'extension de la hiérarchie.

Pour les tests d'interface utilisateur, sealed class en tant que UiState permet de vérifier l'affichage de chaque état : Loading montre un spinner, Content montre des données, Error montre un message d'erreur. Puisque sealed class est fini, la couverture de test de tous les états donne une confiance totale dans la correction de la logique d'interface utilisateur.

Erreurs fréquentes avec Sealed Class

Malgré la simplicité du concept, les développeurs commettent régulièrement des erreurs lors de la conception de hiérarchies de sealed class. Examinons les principaux problèmes et comment les éviter.

  • Sous-types dans différents fichiers — le compilateur ne permettra pas de déclarer une sealed class si les sous-types sont en dehors du fichier. Cette restriction garantit un when exhaustif.
  • Mélange de sealed et open — une sealed class ne peut pas être open en même temps. Si une hiérarchie extensible est nécessaire, utilisez une classe abstraite ordinaire, mais vous sacrifierez l'exhaustivité.
  • Imbrication excessive — sealed class dans sealed class crée une hiérarchie profonde difficile à maintenir. Pour des scénarios simples, deux niveaux suffisent.
  • Else oublié dans when — si une sealed class d'une bibliothèque manque d'exhaustivité, le compilateur n'avertira pas d'une branche manquante. N'ajoutez else qu'intentionnellement.

Questions fréquentes

Une sealed class peut-elle avoir des méthodes abstraites ?

Oui, une sealed class peut contenir des méthodes abstraites, et chaque sous-type est tenu de les implémenter. C'est pratique lorsque toutes les variantes doivent fournir une interface commune mais avec une logique d'exécution différente.

Les sealed classes sont-elles disponibles en Java ?

Dans Java 17+, les classes et interfaces scellées avec le modificateur sealed ont été introduites. Android prend actuellement en charge Java 17 partiellement, mais dans les projets Kotlin, sealed class est disponible depuis Kotlin 1.0 sans restrictions.

Une sealed class peut-elle hériter d'une autre sealed class ?

Oui, une sealed class peut être un sous-type d'une autre. La hiérarchie des sealed classes reste finie : le compilateur connaît tous les sous-types à chaque niveau. Cela permet de construire des classifications d'erreurs détaillées.

Sealed class affecte-t-elle les performances ?

Sealed class ne crée pas de surcharge à l'exécution. Le compilateur optimise les expressions when avec les sealed classes en tables de saut (tableswitch), plus rapides que les chaînes if-else. Les performances sont identiques à enum.

Comment tester les hiérarchies de sealed class ?

Chaque sous-type de sealed class est testé séparément. Puisque sealed class est fini, vous pouvez écrire un test paramétré qui parcourt toutes les variantes. Cela fournit une couverture complète des branches du bloc when.

Résumé

  • Sealed class — classe avec un ensemble fixe de sous-types déclarés dans un seul fichier, permettant une analyse exhaustive de when à la compilation.
  • Chaque sous-type de sealed class peut avoir sa propre structure de données — c'est la principale différence avec enum, où toutes les variantes sont des constantes du même type.
  • Sealed interface (Kotlin 1.5+) prend en charge l'héritage multiple, sealed class ne prend en charge que l'héritage simple. Le choix dépend du besoin d'état partagé.
  • Sealed class est le mécanisme standard pour les hiérarchies d'erreurs type-safe en Kotlin : chaque type d'échec est un sous-type séparé avec des champs pertinents.
  • Patrons principaux dans Android : UI State, Navigation Event et Action/Intent — sont construits sur sealed class pour garantir l'exhaustivité du traitement.
  • Évitez les sous-types dans différents fichiers, l'imbrication excessive et le mélange de sealed avec open — cela viole le contrat d'hiérarchie finie.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi