Sealed Class ist ein spezieller Klassentyp in Kotlin, der die Vererbungshierarchie auf einen festgelegten Satz von Subtypen beschränkt. Alle Subtypen werden in derselben Datei deklariert und sind dem Compiler bekannt, was die Verwendung eines erschöpfenden when-Blocks ohne obligatorischen else-Zweig ermöglicht. Laut Kotlin Docs, 2026 sind versiegelte Klassen ein Schlüsselmechanismus zur Darstellung eingeschränkter Hierarchien wie Zustände, Fehlertypen und UI-Ereignisse.
Wichtige Punkte
Sealed Class (versiegelte Klasse) ist eine Klasse in Kotlin, die mit dem sealed-Modifikator markiert ist. Sie definiert eine eingeschränkte Typhierarchie: alle möglichen Subtypen sind in derselben Datei aufgelistet und der Compiler kennt jeden von ihnen. Dies unterscheidet sealed class von einer gewöhnlichen offenen Klasse, deren Subtypen überall deklariert werden können.
Der Hauptzweck einer sealed class ist die typsichere Darstellung einer endlichen Menge von Varianten. Jeder Subtyp kann eine eigene Datenstruktur haben, was sealed class flexibler als enum macht. Zur Laufzeit ist sealed class eine gewöhnliche abstrakte Klasse; der Compiler legt nur zur Compile-Zeit Beschränkungen auf.
Sealed class ist besonders nützlich in der Android-App-Architektur: UI-Zustände, Netzwerkanforderungsergebnisse, Intent-ähnliche Navigationsereignisse und natürlich Fehlerhierarchien sind typische Anwendungsfälle.
Zur Compile-Zeit wird sealed class in eine Sprungtabelle für when-Ausdrücke optimiert, was sie effizienter als if-else-Ketten macht. In Kombination mit data class kann jeder Subtyp nicht nur Zustand, sondern auch Methoden enthalten, was selbstdokumentierende Domänenmodelle ohne Boilerplate-Code ermöglicht.
Sealed class ist auch effektiv für die Darstellung von Zustandsautomaten in mobilen Anwendungen. Jeder Zustand ist ein separater Subtyp mit eindeutigen Parametern, und Übergänge zwischen Zuständen werden durch when-Ausdrücke gesteuert. Der Compiler garantiert, dass alle möglichen Zustände behandelt werden, wodurch Laufzeitfehler beim Ändern des UI-Zustands oder der Geschïtslogik vermieden werden.
Anfängliche Kotlin-Entwickler verwechseln oft sealed class mit enum, da beide die Menge der Werte einschränken. Es gibt jedoch einen grundlegenden Unterschied: enum ist eine Menge von Konstanten desselben Typs, während sealed class eine Hierarchie verschiedener Typen ist.
Enum ist optimal, wenn alle Varianten Konstanten ohne zusätzliche Struktur sind. Zum Beispiel Wochentage, Bestellstatus oder Aktionstypen ohne Parameter. Jeder enum-Wert ist ein Singleton mit einem festen Namen.
Sealed class wird benötigt, wenn jede Variante eigene Daten hat. Zum Beispiel enthält ein Netzwerkfehler einen Antwortcode, ein Parsingfehler enthält Details und ein Autorisierungsfehler enthält eine Nachricht. Jeder Subtyp einer sealed class ist ein separater Typ mit eindeutigen Feldern.
// Enum — alle Varianten desselben Typs
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — jede Variante mit eigenen Daten
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>()
}
Seit Kotlin 1.5 ist es möglich, sealed interface zu deklarieren. Dies erweitert das Versiegelungskonzept auf Schnittstellen: eine sealed interface hat ebenfalls eine feste Menge von Implementierungen, unterstützt aber Mehrfachvererbung.
Sealed interface ist praktisch, wenn Subtypen mehrere Verträge gleichzeitig implementieren müssen. Zum Beispiel kann ein UI-Ereignis sowohl klickbar als auch verfolgbar sein. Mit sealed class müsste man eine Basisklasse wählen; mit sealed interface implementiert der Subtyp beide.
Sealed class ist eine Klasse, daher kann jeder Subtyp nur einen Eltern haben. Sealed interface löst dieses Problem, kann aber keinen Zustand enthalten. Die Wahl zwischen ihnen hängt von der Aufgabe ab: wird gemeinsame Logik mit Feldern benötigt — verwende sealed class; wird Vertragsflexibilität benötigt — verwende sealed interface.
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>
}
// Der Subtyp implementiert beide Schnittstellen
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
Eine der Hauptanwendungen von sealed class in der mobilen Entwicklung sind typsichere Fehlerhierarchien. Anstatt Ausnahmen verschiedener Typen zu werfen oder eine allgemeine Exception zu verwenden, sammelt sealed class alle möglichen Domänenfehler in einem einzigen Typ.
Erstelle eine sealed class DomainError und liste alle Fehlertypen als Subtypen auf. Jeder Subtyp enthält nur die Daten, die für diesen bestimmten Fehlertyp relevant sind. Der Compiler garantiert, dass du bei der Fehlerbehandlung keine Variante vergisst.
Betrachten wir eine App mit Autorisierung, bei der verschiedene Fehlerszenarien möglich sind: falsches Passwort, Konto gesperrt, Serverproblem. Sealed class kombiniert sie in einem einzigen Typ mit erschöpfender Behandlung.
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 ->
"Verbleibende Versuche: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Zugriff gesperrt bis ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Verbindung prüfen: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Server vorübergehend nicht verfügbar"
}
Sealed class ist zu einem Standardwerkzeug in der Android-Anwendungsarchitektur geworden. Schauen wir uns drei wichtige Muster an, bei denen sealed class in der mobilen Entwicklung unverzichtbar ist.
Erwähnenswert ist auch die Verwendung von sealed class in Clean Architecture. Jede Schicht (data, domain, presentation) verwendet sealed class für ihre Fehlertypen, und Mapper konvertieren eine sealed class in eine andere. Beispielsweise wird DataError aus der Datenschicht in DomainError für die Geschäftslogik und dann in UiState für die Präsentationsschicht gemappt. Dies bewahrt die Typsicherheit auf allen Ebenen der Anwendung und garantiert, dass kein Fehler unbehandelt bleibt.
Das Testen von sealed class erfordert einen besonderen Ansatz, da jeder Subtyp ein separater Typ mit eigenem Zustand ist. Es wird empfohlen, parametrisierte Tests zu schreiben, die alle Subtypen der sealed class durchlaufen. Dies garantiert, dass when-Ausdrücke alle Varianten abdecken, einschließlich neuer, die bei der Erweiterung der Hierarchie hinzugefügt werden.
Für UI-Tests ermöglicht sealed class als UiState die Überprüfung der Anzeige jedes Zustands: Loading zeigt einen Spinner, Content zeigt Daten, Error zeigt eine Fehlermeldung. Da sealed class endlich ist, gibt die Testabdeckung aller Zustände vollständiges Vertrauen in die Korrektheit der UI-Logik.
Trotz der Einfachheit des Konzepts machen Entwickler regelmäßig Fehler beim Entwurf von sealed-class-Hierarchien. Schauen wir uns die Hauptprobleme an und wie man sie vermeidet.
Häufig gestellte Fragen
Ja, eine sealed class kann abstrakte Methoden enthalten, und jeder Subtyp muss sie implementieren. Dies ist praktisch, wenn alle Varianten eine gemeinsame Schnittstelle bieten sollen, aber mit unterschiedlicher Ausführungslogik.
In Java 17+ wurden versiegelte Klassen und Schnittstellen mit dem sealed-Modifikator eingeführt. Android unterstützt Java 17 derzeit teilweise, aber in Kotlin-Projekten ist sealed class seit Kotlin 1.0 ohne Einschränkungen verfügbar.
Ja, eine sealed class kann ein Subtyp einer anderen sein. Die sealed-class-Hierarchie bleibt endlich: der Compiler kennt alle Subtypen auf jeder Ebene. Dies ermöglicht den Aufbau detaillierter Fehlerklassifikationen.
Sealed class verursacht keine Laufzeit-Überhead. Der Compiler optimiert when-Ausdrücke mit sealed classes in Sprungtabellen (tableswitch), die schneller sind als if-else-Ketten. Die Leistung ist identisch mit enum.
Jeder Subtyp einer sealed class wird separat getestet. Da sealed class endlich ist, kann man einen parametrisierten Test schreiben, der alle Varianten durchläuft. Dies bietet eine vollständige Abdeckung der when-Block-Zweige.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch