Nelle prime versioni di Kotlin, per limitare la gerarchia dei tipi venivano utilizzate classi sealed, che permettevano agli sviluppatori di controllare esplicitamente i sotto-tipi consentiti limitando la dichiarazione degli eredi a un singolo file. Per alcune esigenze, questo non era sufficiente, specialmente quando era necessario descrivere gerarchie simili utilizzando interfacce. Per risolvere questo problema, a partire da Kotlin 1.5 è stato introdotto il modificatore sealed per le interfacce.
Le interfacce normali consentono di implementarli ovunque nel progetto, il che spesso impedisce di garantire la totale chiusura della gerarchia dei tipi e di utilizzare controlli di tipo esaustivi (exhaustive when). Ciò può portare a errori di runtime, impossibilità di controllare staticamente tutte le varianti.
Le interfacce sealed consentono di limitare l'elenco delle classi di implementazione all'interno di un singolo file, aumentando la prevedibilità del sistema dei tipi e la sicurezza del pattern matching.
sealed interface NetworkResult class Success(val data: String): NetworkResult class Failure(val error: Throwable): NetworkResult fun handle(result: NetworkResult) = when(result) { is Success -> println("Data: ${result.data}") is Failure -> println("Error: ${result.error}") // Tutte le varianti sono considerate, 'else' non è necessario }
Caratteristiche chiave:
È possibile aggiungere un'interfaccia sealed in un file separato o implementarla al di fuori del file originale?
No. Tutti i tipi che implementano direttamente un'interfaccia sealed devono essere dichiarati nello stesso file, altrimenti il compilatore restituirà un errore.
È consentito a un'interfaccia sealed avere sott'interfacce al di fuori del file di dichiarazione?
No. Anche le sott'interfacce devono trovarsi nello stesso file. L'intera gerarchia è chiusa all'interno del file.
Le interfacce sealed influenzano le prestazioni a runtime?
No, direttamente, ma consentono di utilizzare controlli di tipo sicuri in fase di compilazione, riducendo la probabilità di errori a runtime, semplificando il codice e potenzialmente accelerando i test.
Uno sviluppatore dichiara l'interfaccia sealed NetworkAction in un file, ma le implementazioni sono sparse in diverse classi e file. Il problema viene scoperto tardi: il compilatore segnala una violazione della regola, ed è difficile correggere la struttura, specialmente in un grande progetto.
Vantaggi:
Svantaggi:
L'interfaccia sealed OrderResult e le classi Success/Failure sono dichiarate all'interno di un unico file. Il controllo when è sempre esaustivo, nessun ramo viene omesso.
Vantaggi:
Svantaggi: