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 از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید