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 interval решава овај проблем, али не може садржати стање. Избор између њих зависи од задатка: потребна је заједничка логика са пољима — 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 за своје типове грешака, а мапери претварају један 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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође