Sealed Class — en speciell klass typ i Kotlin som begränsar arvshierarkin till en fast uppsättning subtyper. Alla arvtagare deklareras i samma fil och är kända för kompilatorn, vilket möjliggör användning av ett uttömmande when-block utan obligatorisk else-gren. Enligt Kotlin Docs, 2026 är förseglade klasser en nyckelmekanism för att representera begränsade hierarkier som tillstånd, felttyper och UI-händelser.
Huvudpunkter
Sealed Class (förseglad klass) — är en klass i Kotlin markerad med modifieraren sealed. Den definierar en begränsad typ hierarki: alla möjliga arvtagare listas i samma fil och kompilatorn känner till var och en av dem. Detta skiljer sealed class från en vanlig öppen klass, vars arvtagare kan deklareras var som helst.
Huvudsyftet med sealed class är typsäker representation av en ändlig uppsättning varianter. Varje arvtagare kan ha sin egen datastruktur, vilket gör sealed class mer flexibel än enum. Vid körning är sealed class en vanlig abstrakt klass, kompilatorn tillämpar begränsningar endast vid kompilering.
Sealed class är särskilt användbar i Android-arkitektur för applikationer: UI-tillstånd, nätverksförfrågningsresultat, Intent-liknande navigeringshändelser och naturligtvis felhierarkier — typiska tillämpningsscenarier.
Vid kompilering optimeras sealed class till en övergångstabell för when-uttryck, vilket gör den effektivare än if-else-kedjor. I kombination med data class kan varje arvtagare inte bara innehålla tillstånd, utan även metoder, vilket möjliggör byggande av självdokumenterande domänmodeller utan boilerplate-kod.
Sealed class är också effektiv för att representera ändliga tillståndsmaskiner (state machine) i mobilapplikationer. Varje tillstånd — en separat arvtagare med unika parametrar, och övergångar mellan tillstånd styrs via when-uttrycket. Kompilatorn garanterar att alla möjliga tillstånd har bearbetats, vilket eliminerar körningsfel vid ändring av UI-tillstånd eller affärslogik.
Börjande Kotlin-utvecklare förväxlar ofta sealed class med enum, eftersom båda begränsar värdeuppsättningen. Men det finns en grundläggande skillnad mellan dem: enum — en uppsättning konstanter av samma typ, sealed class — en hierarki av olika typer.
Enum är optimal när alla varianter är konstanter utan ytterligare struktur. Till exempel veckodagar, orderstatus eller åtgärdstyper utan parametrar. Varje enum-värde är en singleton med ett fast namn.
Sealed class behövs när varje variant har egna data. Till exempel innehåller ett nätverksfel svarskod, ett parsningsfel — detaljer, och ett auktoriseringsfel — ett meddelande. Varje arvtagare till sealed class är en separat typ med unika fält.
// Enum — alla varianter av en typ
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — varje variant med egna data
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>()
}
Från Kotlin 1.5 finns möjligheten att deklarera sealed interface. Detta utökar sealed-konceptet till gränssnitt: sealed interface har också en fast uppsättning implementationer, men stöder multipelt arv.
Sealed interface är praktiskt när arvtagare måste implementera flera kontrakt samtidigt. Till exempel kan en UI-händelse vara både klickbar och spårbar. Med sealed class skulle man behöva välja en basklass, med sealed interface implementerar arvtagaren båda.
Sealed class är en klass, så varje arvtagare kan bara ha en förälder. Sealed interface löser detta problem, men kan inte innehålla tillstånd. Valet mellan dem beror på uppgiften: gemensam logik med fält behövs — sealed class, flexibilitet i kontrakt behövs — 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>
}
// Arvtagaren implementerar båda gränssnitten
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
En av de viktigaste tillämpningarna av sealed class inom mobilutveckling — typsäker felhierarki. Istället för att kasta undantag av olika typer eller använda allmänt Exception, samlar sealed class alla möjliga domänfel i en typ.
Skapa en sealed class DomainError och lista alla typer av fel som arvtagare. Varje arvtagare innehåller endast de data som är meningsfulla för den feltypen. Kompilatorn garanterar att du vid felhantering inte kommer att glömma någon variant.
Betrakta en applikation med auktorisering, där olika fel scenarier är möjliga: felaktigt lösenord, kontoblockering, serverproblem. Sealed class förenar dem i en enda typ med uttömmande hantering.
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 ->
"Återstående försök: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Åtkomst blockerad till ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Kontrollera anslutningen: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Servern är tillfälligt otillgänglig"
}
Sealed class har blivit ett standardverktyg i arkitekturen för Android-applikationer. Låt oss titta på tre viktiga mönster där sealed class är oumbärlig inom mobilutveckling.
Separat är det värt att nämna tillämpningen av sealed class i Clean Architecture. Varje lager (data, domain, presentation) använder sealed class för sina felttyper, och mappare omvandlar en sealed class till en annan. Till exempel mappas DataError från datalagret till DomainError för affärslogik och sedan till UiState för presentationslagret. Detta bevarar typsäkerheten på alla nivåer i applikationen och garanterar att inget fel förblir obearbetat.
Testning av sealed class kräver ett speciellt tillvägagångssätt, eftersom varje arvtagare är en separat typ med eget tillstånd. Det rekommenderas att skriva parameteriserade tester som går igenom alla arvtagare av sealed class. Detta garanterar att when-uttryck täcker alla varianter, inklusive nya som lagts till vid utökning av hierarkin.
För UI-tester gör sealed class som UiState det möjligt att kontrollera visningen av varje tillstånd: Loading visar en spinner, Content — data, Error — ett felmeddelande. Eftersom sealed class är ändlig ger testtäckning av alla tillstånd fullständig säkerhet om UI-logikens korrekthet.
Trots konceptets enkelhet gör utvecklare regelbundet misstag vid utformning av sealed class hierarkier. Låt oss titta på de viktigaste problemen och hur man undviker dem.
Vanliga frågor
Ja, sealed class kan innehålla abstrakta metoder, och varje arvtagare måste implementera dem. Detta är praktiskt när alla varianter ska tillhandahålla ett enhetligt gränssnitt, men med olika exekveringslogik.
I Java 17+ har förseglade klasser och gränssnitt med modifieraren sealed dykt upp. Android stöder för närvarande Java 17 delvis, men i Kotlin-projekt är sealed class tillgänglig från Kotlin 1.0 utan begränsningar.
Ja, en sealed class kan vara arvtagare till en annan. Sealed class hierarkin förblir ändlig: kompilatorn känner till alla arvtagare på varje nivå. Detta möjliggör byggande av detaljerade felklassificeringar.
Sealed class skapar ingen overhead vid körning. Kompilatorn optimerar when-uttryck med sealed class till övergångstabeller (tableswitch), vilket är snabbare än if-else-kedjor. Prestandan är identisk med enum.
Varje arvtagare till sealed class testas separat. Eftersom sealed class är ändlig kan ett parameteriserat test skrivas som går igenom alla varianter. Detta ger fullständig täckning av grenarna i when-block.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också