Sealed Class — speciální typ třídy v Kotlinu, který omezuje hierarchii dědičnosti na pevnou sadu podtypů. Všichni dědicové jsou deklarováni ve stejném souboru a jsou známí kompilátoru, což umožňuje použití vyčerpávajícího bloku when bez povinné else větve. Podle Kotlin Docs, 2026 jsou zapečetěné třídy klíčovým mechanismem pro reprezentaci omezených hierarchií, jako jsou stavy, typy chyb a události UI.
Hlavní body
Sealed Class (zapečetěná třída) — je třída v Kotlinu označená modifikátorem sealed. Definuje omezenou hierarchii typů: všichni možní dědicové jsou uvedeni ve stejném souboru a kompilátor o každém z nich ví. To odlišuje sealed class od běžné otevřené třídy, jejíž dědicové mohou být deklarováni kdekoli.
Hlavním účelem sealed class je typově bezpečná reprezentace konečné sady variant. Každý dědic může mít vlastní datovou strukturu, což činí sealed class flexibilnější než enum. Za běhu je sealed class běžná abstraktní třída, kompilátor ukládá omezení pouze ve fázi kompilace.
Sealed class je zvláště užitečný v architektuře Android aplikací: stavy UI, výsledky síťových požadavků, události navigace typu Intent a samozřejmě hierarchie chyb — typické scénáře použití.
Při kompilaci je sealed class optimalizován do přechodové tabulky pro výrazy when, což jej činí výkonnějším než řetězce if-else. V kombinaci s data class může každý dědic obsahovat nejen stav, ale také metody, což umožňuje vytvářet samodokumentující doménové modely bez boilerplate kódu.
Sealed class je také účinný pro reprezentaci konečných automatů (state machine) v mobilních aplikacích. Každý stav — samostatný dědic s jedinečnými parametry a přechody mezi stavy jsou řízeny prostřednictvím výrazu when. Kompilátor zaručuje, že všechny možné stavy byly zpracovány, což eliminuje chyby za běhu při změně stavu UI nebo obchodní logiky.
Začínající vývojáři v Kotlinu často zaměňují sealed class s enum, protože oba omezují sadu hodnot. Mezi nimi je však zásadní rozdíl: enum — je sada konstant stejného typu, sealed class — hierarchie různých typů.
Enum je optimální, když všechny varianty jsou konstanty bez další struktury. Například dny v týdnu, stavy objednávky nebo typy akcí bez parametrů. Každá hodnota enum je singleton s pevným názvem.
Sealed class je potřeba, když každá varianta má vlastní data. Například chyba sítě obsahuje kód odpovědi, chyba parsování — podrobnosti a chyba autorizace — zprávu. Každý dědic sealed class je samostatný typ s jedinečnými poli.
// Enum — všechny varianty jednoho typu
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — každá varianta s vlastními daty
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>()
}
Od Kotlin 1.5 existuje možnost deklarovat sealed interface. To rozšiřuje koncept sealed na rozhraní: sealed interface má také pevnou sadu implementací, ale podporuje vícenásobnou dědičnost.
Sealed interface je vhodný, když dědicové musí implementovat více kontraktů současně. Například událost UI může být současně klikatelná a sledovatelná. U sealed class byste museli vybrat jednu základní třídu, u sealed interface dědic implementuje obě.
Sealed class je třída, takže každý dědic může mít pouze jednoho rodiče. Sealed interface tento problém řeší, ale nemůže obsahovat stav. Volba mezi nimi závisí na úkolu: potřebujete společnou logiku s poli — sealed class, potřebujete flexibilitu kontraktů — 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>
}
// Dědic implementuje obě rozhraní
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
Jedno z hlavních použití sealed class v mobilním vývoji — typově bezpečná hierarchie chyb. Místo vyhazování výjimek různých typů nebo používání obecného Exception, sealed class shromažďuje všechny možné doménové chyby do jednoho typu.
Vytvořte sealed class DomainError a vyjmenujte všechny typy selhání jako dědice. Každý dědic obsahuje pouze data, která mají význam pro daný typ chyby. Kompilátor zaručuje, že při zpracování chyby nezapomenete žádnou variantu.
Uvažujme aplikaci s autorizací, kde jsou možné různé scénáře selhání: nesprávné heslo, blokace účtu, problém se serverem. Sealed class je spojuje do jednoho typu s vyčerpávajícím zpracováním.
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 ->
"Zbývá pokusů: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Přístup zablokován do ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Zkontrolujte připojení: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Server je dočasně nedostupný"
}
Sealed class se stal standardním nástrojem v architektuře aplikací pro Android. Podívejme se na tři klíčové vzory, kde je sealed class v mobilním vývoji nepostradatelný.
Samostatně stojí za zmínku použití sealed class v Clean Architecture. Každá vrstva (data, domain, presentation) používá sealed class pro své typy chyb a mappery převádějí jeden sealed class na druhý. Například DataError z datové vrstvy je mapován na DomainError pro obchodní logiku a poté na UiState pro prezentační vrstvu. To zachovává typovou bezpečnost na všech úrovních aplikace a zaručuje, že žádná chyba nezůstane nezpracována.
Testování sealed class vyžaduje zvláštní přístup, protože každý dědic je samostatný typ s vlastním stavem. Doporučuje se psát parametrizované testy, které procházejí všechny dědice sealed class. To zaručuje, že výrazy when pokrývají všechny varianty, včetně nových přidaných při rozšiřování hierarchie.
Pro UI testy sealed class jako UiState umožňuje kontrolu zobrazení každého stavu: Loading ukazuje spinner, Content — data, Error — chybovou zprávu. Protože je sealed class konečný, testovací pokrytí všech stavů dává plnou jistotu správnosti UI logiky.
Navzdory jednoduchosti konceptu vývojáři pravidelně dělají chyby při navrhování hierarchií sealed class. Podívejme se na hlavní problémy a způsoby, jak se jim vyhnout.
Často kladené otázky
Ano, sealed class může obsahovat abstraktní metody a každý dědic je povinen je implementovat. To je užitečné, když všechny varianty musí poskytovat jednotné rozhraní, ale s odlišnou logikou provedení.
V Java 17+ se objevily zapečetěné třídy a rozhraní s modifikátorem sealed. Android zatím podporuje Java 17 částečně, ale v Kotlin projektech je sealed class dostupný od Kotlin 1.0 bez omezení.
Ano, jeden sealed class může být dědicem druhého. Hierarchie sealed class zůstává konečná: kompilátor zná všechny dědice na každé úrovni. To umožňuje vytvářet podrobné klasifikace chyb.
Sealed class nevytváří režii za běhu. Kompilátor optimalizuje výrazy when s sealed class do přechodových tabulek (tableswitch), což je rychlejší než řetězce if-else. Výkon je identický s enum.
Každý dědic sealed class se testuje samostatně. Protože je sealed class konečný, lze napsat parametrizovaný test, který prochází všechny varianty. To poskytuje úplné pokrytí větví bloků when.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také