Sealed Class — cos'è, principio di funzionamento e applicazione

Autore: IT Sectr Pubblicato: 2026-05-26 Tempo di lettura: 8 min

Sealed Class è un tipo speciale di classe in Kotlin che limita la gerarchia di ereditarietà a un insieme fisso di sottotipi. Tutti i sottotipi sono dichiarati nello stesso file e sono noti al compilatore, consentendo di utilizzare un blocco when esaustivo senza un ramo else obbligatorio. Secondo Kotlin Docs, 2026, le classi sigillate sono un meccanismo chiave per rappresentare gerarchie limitate, come stati, tipi di errore ed eventi dell'interfaccia utente.

Punti chiave

  • Sealed Class — classe con un insieme fisso di sottotipi dichiarati nello stesso file.
  • Exhaustive when — il compilatore verifica che tutti i sottotipi siano gestiti, eliminando rami else dimenticati.
  • Sealed interface — Kotlin 1.5+ supporta interfacce sigillate per ereditarietà multipla.
  • Gerarchia di errori — sealed class è il metodo standard per la gestione degli errori type-safe in Kotlin.
  • Differenza da enum — ogni sottotipo di sealed class può contenere uno stato unico e un diverso numero di campi.

Cos'è Sealed Class?

Sealed Class (classe sigillata) è una classe in Kotlin contrassegnata con il modificatore sealed. Definisce una gerarchia di tipi limitata: tutti i possibili sottotipi sono elencati nello stesso file e il compilatore conosce ciascuno di essi. Questo distingue sealed class da una normale classe aperta i cui sottotipi possono essere dichiarati ovunque.

Lo scopo principale di sealed class è la rappresentazione type-safe di un insieme finito di varianti. Ogni sottotipo può avere una propria struttura dati, rendendo sealed class più flessibile di enum. In fase di esecuzione, sealed class è una normale classe astratta; il compilatore impone restrizioni solo in fase di compilazione.

Sealed class è particolarmente utile nell'architettura delle applicazioni Android: stati dell'interfaccia utente, risultati di richieste di rete, eventi di navigazione simili a Intent e, naturalmente, gerarchie di errori sono casi d'uso tipici.

In fase di compilazione, sealed class viene ottimizzata in una tabella di salto per le espressioni when, rendendola più efficiente delle catene if-else. Combinata con data class, ogni sottotipo può contenere non solo stato ma anche metodi, consentendo di costruire modelli di dominio auto-documentanti senza codice boilerplate.

Sealed class è anche efficace per rappresentare macchine a stati nelle applicazioni mobili. Ogni stato è un sottotipo separato con parametri unici e le transizioni tra stati sono controllate tramite espressioni when. Il compilatore garantisce che tutti gli stati possibili siano gestiti, eliminando errori in fase di esecuzione quando si cambia lo stato dell'interfaccia utente o la logica di business.

Sealed Class vs Enum: differenze principali

Gli sviluppatori Kotlin principianti spesso confondono sealed class con enum, poiché entrambi limitano l'insieme dei valori. Tuttavia, c'è una differenza fondamentale: enum è un insieme di costanti dello stesso tipo, mentre sealed class è una gerarchia di tipi diversi.

Quando scegliere enum

Enum è ottimale quando tutte le varianti sono costanti senza struttura aggiuntiva. Ad esempio, giorni della settimana, stati degli ordini o tipi di azioni senza parametri. Ogni valore enum è un singleton con un nome fisso.

Quando scegliere sealed class

Sealed class è necessaria quando ogni variante ha i propri dati. Ad esempio, un errore di rete contiene un codice di risposta, un errore di parsing contiene dettagli e un errore di autorizzazione contiene un messaggio. Ogni sottotipo di sealed class è un tipo separato con campi unici.

kotlin
// Enum — tutte le varianti dello stesso tipo
enum class Status { LOADING, SUCCESS, ERROR }

// Sealed class — ogni variante con i propri dati
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

Da Kotlin 1.5, è possibile dichiarare sealed interface. Questo estende il concetto di sigillatura alle interfacce: una sealed interface ha anche un insieme fisso di implementazioni ma supporta l'ereditarietà multipla.

Quando usare sealed interface

Sealed interface è comoda quando i sottotipi devono implementare più contratti contemporaneamente. Ad esempio, un evento dell'interfaccia utente può essere sia cliccabile che tracciabile. Con sealed class, dovresti scegliere una classe base; con sealed interface, il sottotipo implementa entrambi.

Limitazioni di sealed class

Sealed class è una classe, quindi ogni sottotipo può avere un solo genitore. Sealed interface risolve questo problema ma non può contenere stato. La scelta tra loro dipende dal compito: se serve logica condivisa con campi — usa sealed class; se serve flessibilità di contratti — usa 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>
}

// Il sottotipo implementa entrambe le interfacce
data class LoginClicked(
    override val name: String = "login_click",
    override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent

Sealed Class per gerarchie di errori nello sviluppo mobile

Uno degli usi principali di sealed class nello sviluppo mobile è la gerarchia di errori type-safe. Invece di lanciare eccezioni di diversi tipi o usare un'Exception generica, sealed class raccoglie tutti i possibili errori di dominio in un unico tipo.

Come costruire una gerarchia di errori

Crea una sealed class DomainError ed elenca tutti i tipi di fallimento come sottotipi. Ogni sottotipo contiene solo i dati rilevanti per quel particolare tipo di errore. Il compilatore garantisce che durante la gestione dell'errore non dimenticherai alcuna variante.

Esempio: gestione degli errori di autenticazione

Considera un'applicazione con autorizzazione dove sono possibili diversi scenari di fallimento: password errata, account bloccato, problema del server. Sealed class li combina in un unico tipo con gestione esaustiva.

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 ->
        "Tentativi rimanenti: ${3 - error.attempts}"
    is AuthError.AccountBlocked ->
        "Accesso bloccato fino al ${Date(error.until)}"
    is AuthError.NetworkFailure ->
        "Controlla la connessione: ${error.cause.localizedMessage}"
    AuthError.ServerError ->
        "Server temporaneamente non disponibile"
}

Pattern di utilizzo di Sealed Class in Android

Sealed class è diventata uno strumento standard nell'architettura delle applicazioni Android. Vediamo tre pattern principali in cui sealed class è indispensabile nello sviluppo mobile.

  • UI State — rappresentazione dello schermo come macchina a stati: Loading, Content, Error. Ogni stato contiene i propri dati, e sealed class garantisce che tutte le transizioni siano gestite.
  • Navigation Event — sealed class al posto delle costanti di navigazione: ogni schermata è un sottotipo separato con parametri di percorso. Il compilatore verifica i tipi degli argomenti.
  • Action/Intent — il pattern Unidirectional Data Flow usa sealed class per rappresentare tutte le azioni che un utente può eseguire su una schermata.

Vale anche la pena notare l'uso di sealed class in Clean Architecture. Ogni strato (data, domain, presentation) usa sealed class per i propri tipi di errore, e i mapper convertono una sealed class in un'altra. Ad esempio, DataError dallo strato dati viene mappato in DomainError per la logica di business, e poi in UiState per lo strato di presentazione. Questo preserva la sicurezza dei tipi a tutti i livelli dell'applicazione e garantisce che nessun errore rimanga non gestito.

Test delle gerarchie Sealed Class

Il testing di sealed class richiede un approccio speciale, poiché ogni sottotipo è un tipo separato con il proprio stato. Si consiglia di scrivere test parametrizzati che iterano su tutti i sottotipi di sealed class. Questo garantisce che le espressioni when coprano tutte le varianti, incluse quelle nuove aggiunte quando si estende la gerarchia.

Per i test dell'interfaccia utente, sealed class come UiState consente di verificare la visualizzazione di ogni stato: Loading mostra uno spinner, Content mostra dati, Error mostra un messaggio di errore. Poiché sealed class è finito, la copertura dei test di tutti gli stati dà completa fiducia nella correttezza della logica dell'interfaccia utente.

Errori comuni quando si lavora con Sealed Class

Nonostante la semplicità del concetto, gli sviluppatori commettono regolarmente errori nella progettazione di gerarchie di sealed class. Vediamo i problemi principali e come evitarli.

  • Sottotipi in file diversi — il compilatore non permetterà di dichiarare una sealed class se i sottotipi sono al di fuori del file. Questa restrizione garantisce un when esaustivo.
  • Mescolare sealed e open — una sealed class non può essere open contemporaneamente. Se è necessaria una gerarchia estensibile, usa una normale classe astratta, ma sacrificherai l'esaustività.
  • Annidamento eccessivo — sealed class dentro sealed class crea una gerarchia profonda difficile da mantenere. Per scenari semplici, due livelli sono sufficienti.
  • Else dimenticato in when — se una sealed class da una libreria non ha esaustività, il compilatore non avviserà di un ramo mancante. Aggiungi else solo intenzionalmente.

Domande frequenti

Una sealed class può avere metodi astratti?

Sì, una sealed class può contenere metodi astratti e ogni sottotipo è obbligato a implementarli. Questo è comodo quando tutte le varianti devono fornire un'interfaccia comune ma con logica di esecuzione diversa.

Le sealed class sono disponibili in Java?

In Java 17+, sono state introdotte classi e interfacce sigillate con il modificatore sealed. Android attualmente supporta Java 17 parzialmente, ma nei progetti Kotlin, sealed class è disponibile da Kotlin 1.0 senza restrizioni.

Una sealed class può ereditare da un'altra sealed class?

Sì, una sealed class può essere un sottotipo di un'altra. La gerarchia delle sealed class rimane finita: il compilatore conosce tutti i sottotipi a ogni livello. Questo consente di costruire classificazioni dettagliate degli errori.

Sealed class influisce sulle prestazioni?

Sealed class non crea overhead in fase di esecuzione. Il compilatore ottimizza le espressioni when con sealed class in tabelle di salto (tableswitch), più veloci delle catene if-else. Le prestazioni sono identiche a enum.

Come testare le gerarchie di sealed class?

Ogni sottotipo di sealed class viene testato separatamente. Poiché sealed class è finito, puoi scrivere un test parametrizzato che iteri su tutte le varianti. Questo fornisce una copertura completa dei rami del blocco when.

Riepilogo

  • Sealed class — classe con un insieme fisso di sottotipi dichiarati in un file, consentendo un'analisi esaustiva di when in fase di compilazione.
  • Ogni sottotipo di sealed class può avere una propria struttura dati — questa è la principale differenza da enum, dove tutte le varianti sono costanti dello stesso tipo.
  • Sealed interface (Kotlin 1.5+) supporta l'ereditarietà multipla, sealed class supporta solo l'ereditarietà singola. La scelta dipende dalla necessità di stato condiviso.
  • Sealed class è il meccanismo standard per gerarchie di errori type-safe in Kotlin: ogni tipo di fallimento è un sottotipo separato con campi pertinenti.
  • Pattern principali in Android: UI State, Navigation Event e Action/Intent — sono costruiti su sealed class per garantire la completezza della gestione.
  • Evita sottotipi in file diversi, annidamento eccessivo e mescolare sealed con open — questo viola il contratto di gerarchia finita.

Svilupperemo un'applicazione mobile chiavi in mano

IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.

Discuti il progetto

Leggi anche