Sealed Class — специален тип клас в Kotlin, който ограничава йерархията на наследяване до фиксиран набор от подтипове. Всички наследници се декларират в същия файл и са известни на компилатора, което позволява използването на изчерпателен when-блок без задължителна else-разклонение. Според Kotlin Docs, 2026, запечатаните класове са ключов механизъм за представяне на ограничени йерархии, като състояния, типове грешки и UI събития.
Основни точки
Sealed Class (запечатан клас) — това е клас в Kotlin, маркиран с модификатора sealed. Той дефинира ограничена йерархия от типове: всички възможни наследници са изброени в същия файл и компилаторът знае за всеки от тях. Това отличава sealed class от обикновения отворен клас, чиито наследници могат да бъдат декларирани навсякъде.
Основната цел на sealed class е типобезопасно представяне на краен набор от варианти. Всеки наследник може да има собствена структура от данни, което прави sealed class по-гъвкав от enum. По време на изпълнение sealed class е обикновен абстрактен клас, компилаторът налага ограничения само на етапа на компилиране.
Sealed class е особено полезен в архитектурата на Android приложения: UI състояния, резултати от мрежови заявки, Intent-подобни навигационни събития и, разбира се, йерархии от грешки — типични сценарии на приложение.
При компилиране sealed class се оптимизира до таблица за преход за when изрази, което го прави по-ефективен от if-else вериги. В комбинация с data class всеки наследник може да съдържа не само състояние, но и методи, което позволява изграждането на самодокументиращи се домейн модели без boilerplate код.
Sealed class е също ефективен за представяне на крайни автомати (state machine) в мобилни приложения. Всяко състояние — отделен наследник с уникални параметри, а преходите между състоянията се контролират чрез when израза. Компилаторът гарантира, че всички възможни състояния са обработени, което елиминира грешки по време на изпълнение при промяна на UI състояние или бизнес логика.
Начинаещите програмисти на Kotlin често бъркат sealed class с enum, тъй като и двата ограничават набора от стойности. Но съществува фундаментална разлика между тях: enum — набор от константи от един тип, sealed class — йерархия от различни типове.
Enum е оптимален, когато всички варианти са константи без допълнителна структура. Например дните от седмицата, статуси на поръчка или типове действия без параметри. Всяка enum стойност е singleton с фиксирано име.
Sealed class е необходим, когато всеки вариант има собствени данни. Например мрежова грешка съдържа код на отговор, грешка при парсиране — детайли, а грешка при авторизация — съобщение. Всеки наследник на sealed class е отделен тип с уникални полета.
// Enum — всички варианти от един тип
enum class Status { LOADING, SUCCESS, ERROR }
// Sealed class — всеки вариант със свои данни
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>()
}
От Kotlin 1.5 съществува възможност за деклариране на sealed interface. Това разширява концепцията sealed към интерфейси: sealed interface също има фиксиран набор от имплементации, но поддържа множествено наследяване.
Sealed interface е удобен, когато наследниците трябва да имплементират множество договори едновременно. Например UI събитие може да бъде едновременно кликаемо и проследимо. С sealed class трябваше да изберете един базов клас, с sealed interface наследникът имплементира и двата.
Sealed class е клас, така че всеки наследник може да има само един родител. Sealed interface решава този проблем, но не може да съдържа състояние. Изборът между тях зависи от задачата: нужна е обща логика с полета — sealed class, нужна е гъвкавост на договорите — 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>
}
// Наследникът имплементира и двата интерфейса
data class LoginClicked(
override val name: String = "login_click",
override val params: Map<String, Any> = emptyMap()
) : ScreenEvent, AnalyticsEvent
Едно от основните приложения на sealed class в мобилната разработка — типобезопасна йерархия от грешки. Вместо да хвърляте изключения от различни типове или да използвате общ Exception, sealed class събира всички възможни домейн грешки в един тип.
Създайте sealed class DomainError и избройте всички видове повреди като наследници. Всеки наследник съдържа само данните, които имат смисъл за този тип грешка. Компилаторът гарантира, че при обработка на грешката няма да забравите нито един вариант.
Разгледайте приложение с авторизация, където са възможни различни сценарии на повреда: грешна парола, блокиране на акаунт, проблем със сървъра. Sealed class ги обединява в един тип с изчерпателна обработка.
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 ->
"Оставащи опити: ${3 - error.attempts}"
is AuthError.AccountBlocked ->
"Достъпът е блокиран до ${Date(error.until)}"
is AuthError.NetworkFailure ->
"Проверете връзката: ${error.cause.localizedMessage}"
AuthError.ServerError ->
"Сървърът е временно недостъпен"
}
Sealed class се превърна в стандартен инструмент в архитектурата на Android приложения. Нека разгледаме три ключови модела, където sealed class е незаменим в мобилната разработка.
Отделно заслужава да се спомене приложението на sealed class в Clean Architecture. Всеки слой (data, domain, presentation) използва sealed class за своите типове грешки, а mapper-ите преобразуват един sealed class в друг. Например DataError от слоя данни се мапва към DomainError за бизнес логиката, а след това към UiState за презентационния слой. Това запазва типовата безопасност на всички нива на приложението и гарантира, че никоя грешка няма да остане необработена.
Тестването на sealed class изисква специален подход, тъй като всеки наследник е отделен тип със собствено състояние. Препоръчва се писане на параметризирани тестове, които преминават през всички наследници на sealed class. Това гарантира, че when изразите покриват всички варианти, включително новите, добавени при разширяване на йерархията.
За UI тестове sealed class като UiState позволява проверка на визуализацията на всяко състояние: Loading показва спинър, Content — данни, Error — съобщение за грешка. Тъй като sealed class е краен, тестовото покритие на всички състояния дава пълна увереност в коректността на UI логиката.
Въпреки простотата на концепцията, програмистите редовно допускат грешки при проектирането на sealed class йерархии. Нека разгледаме основните проблеми и начините за избягването им.
Често задавани въпроси
Да, sealed class може да съдържа абстрактни методи и всеки наследник е длъжен да ги имплементира. Това е удобно, когато всички варианти трябва да предоставят единен интерфейс, но с различна логика на изпълнение.
В Java 17+ се появиха запечатани класове и интерфейси с модификатор sealed. Android засега поддържа Java 17 частично, но в Kotlin проекти sealed class е достъпен от Kotlin 1.0 без ограничения.
Да, един sealed class може да бъде наследник на друг. Йерархията на sealed class остава крайна: компилаторът познава всички наследници на всяко ниво. Това позволява изграждане на детайлни класификации на грешки.
Sealed class не създава допълнителни разходи по време на изпълнение. Компилаторът оптимизира when изрази със sealed class в таблици за преход (tableswitch), което е по-бързо от if-else вериги. Производителността е идентична с enum.
Всеки наследник на sealed class се тества отделно. Тъй като sealed class е краен, може да се напише параметризиран тест, който преминава през всички варианти. Това дава пълно покритие на разклоненията на when блоковете.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също