Sealed Class — چیست، اصل کار و کاربرد

نویسنده: IT Sectr منتشر شده: 2026-05-26 زمان مطالعه: 8 دقیقه

Sealed Class — نوع خاصی از کلاس در Kotlin است که سلسله‌مراتب وراثت را به مجموعه ثابتی از زیرنوع‌ها محدود می‌کند. همه وراث در همان فایل اعلام می‌شوند و برای کامپایلر شناخته شده هستند، که امکان استفاده از بلوک when جامع بدون شاخه else اجباری را فراهم می‌کند. به گفته Kotlin Docs, 2026، کلاس‌های مهر و موم شده — مکانیزم کلیدی برای نمایش سلسله‌مراتب محدود مانند حالت‌ها، انواع خطاها و رویدادهای UI هستند.

نکات اصلی

  • Sealed Class — کلاسی با مجموعه ثابتی از وراث که در همان فایل اعلام شده‌اند.
  • Exhaustive when — کامپایلر بررسی می‌کند که همه زیرنوع‌ها پردازش شده‌اند و شاخه‌های else فراموش شده را حذف می‌کند.
  • Sealed interface — Kotlin 1.5+ از رابط‌های مهر و موم شده برای وراثت چندگانه پشتیبانی می‌کند.
  • سلسله‌مراتب خطاها — sealed class روش استاندارد مدیریت نوع‌امن خطاها در Kotlin است.
  • تفاوت با enum — هر وارث sealed class می‌تواند حالت منحصربه‌فرد و تعداد متفاوتی از فیلدها داشته باشد.

Sealed Class چیست؟

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 یا منطق کسب‌وکار حذف می‌کند.

Sealed Class و Enum: تفاوت‌های کلیدی

توسعه‌دهندگان مبتدی Kotlin اغلب sealed class را با enum اشتباه می‌گیرند، زیرا هر دو مجموعه مقادیر را محدود می‌کنند. با این حال تفاوت اساسی بین آنها وجود دارد: enum — مجموعه‌ای از ثابت‌های یک نوع، sealed class — سلسله‌مراتب انواع مختلف.

چه زمانی enum را انتخاب کنیم

Enum زمانی بهینه است که همه گزینه‌ها ثابت‌هایی بدون ساختار اضافی هستند. به عنوان مثال، روزهای هفته، وضعیت‌های سفارش یا انواع اقدامات بدون پارامتر. هر مقدار enum یک singleton با نام ثابت است.

چه زمانی sealed class را انتخاب کنیم

Sealed class زمانی نیاز است که هر گزینه داده‌های خاص خود را دارد. به عنوان مثال، خطای شبکه شامل کد پاسخ، خطای تجزیه — جزئیات، و خطای احراز هویت — پیام. هر وارث sealed class یک نوع جداگانه با فیلدهای منحصربه‌فرد است.

kotlin
// 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>()
}

Sealed Interface در مقابل Sealed Class

از Kotlin 1.5 امکان اعلام sealed interface فراهم شده است. این مفهوم sealed را به رابط‌ها گسترش می‌دهد: sealed interface نیز مجموعه ثابتی از پیاده‌سازی‌ها دارد، اما از وراثت چندگانه پشتیبانی می‌کند.

چه زمانی از sealed interface استفاده کنیم

Sealed interface زمانی مناسب است که وراث باید همزمان چندین قرارداد را پیاده‌سازی کنند. به عنوان مثال، یک رویداد UI می‌تواند همزمان قابل کلیک و قابل ردیابی باشد. با sealed class باید یک کلاس پایه انتخاب کرد، با sealed interface وارث هر دو را پیاده‌سازی می‌کند.

محدودیت‌های sealed class

Sealed class یک کلاس است، بنابراین هر وارث فقط می‌تواند یک والد داشته باشد. Sealed interface این مشکل را حل می‌کند، اما نمی‌تواند حالت را ذخیره کند. انتخاب بین آنها به وظیفه بستگی دارد: منطق مشترک با فیلدها نیاز است — sealed class، انعطاف‌پذیری قراردادها نیاز است — sealed interface.

kotlin
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 برای سلسله‌مراتب خطاها در توسعه موبایل

یکی از کاربردهای اصلی sealed class در توسعه موبایل — سلسله‌مراتب نوع‌امن خطاها. به جای پرتاب استثناهای انواع مختلف یا استفاده از Exception عمومی، sealed class تمام خطاهای ممکن دامنه را در یک نوع جمع‌آوری می‌کند.

چگونه سلسله‌مراتب خطاها را بسازیم

یک sealed class به نام DomainError ایجاد کنید و همه انواع خرابی را به عنوان وراث فهرست کنید. هر وارث فقط داده‌هایی را شامل می‌شود که برای آن نوع خطا معنا دارند. کامپایلر تضمین می‌کند که هنگام پردازش خطا هیچ گزینه‌ای را فراموش نکنید.

مثال: مدیریت خطاهای احراز هویت

برنامه‌ای با احراز هویت را در نظر بگیرید که سناریوهای مختلف خرابی ممکن است: رمز عبور اشتباه، مسدود شدن حساب، مشکل سرور. Sealed class آنها را در یک نوع واحد با مدیریت جامع ترکیب می‌کند.

kotlin
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 به ابزاری استاندارد در معماری برنامه‌های Android تبدیل شده است. سه الگوی کلیدی را بررسی می‌کنیم که sealed class در توسعه موبایل در آنها غیرقابل جایگزین است.

  • UI State — نمایش صفحه به عنوان ماشین حالت محدود: Loading، Content، Error. هر حالت داده‌های خود را شامل می‌شود و sealed class تضمین می‌کند که همه انتقال‌ها پردازش شده‌اند.
  • Navigation Event — sealed class به جای ثابت‌های ناوبری: هر صفحه وارث جداگانه با پارامترهای مسیر است. کامپایلر انواع آرگومان‌ها را بررسی می‌کند.
  • Action/Intent — الگوی Unidirectional Data Flow از sealed class برای نمایش تمام اقداماتی که کاربر می‌تواند در صفحه انجام دهد استفاده می‌کند.

به طور جداگانه باید به کاربرد sealed class در Clean Architecture اشاره کرد. هر لایه (data، domain، presentation) از sealed class برای انواع خطاهای خود استفاده می‌کند و mapperها یک sealed class را به دیگری تبدیل می‌کنند. به عنوان مثال، DataError از لایه داده به DomainError برای منطق کسب‌وکار و سپس به UiState برای لایه ارائه نگاشت می‌شود. این کار نوع‌امنی را در تمام سطوح برنامه حفظ می‌کند و تضمین می‌کند که هیچ خطایی پردازش‌نشده باقی نمی‌ماند.

تست سلسله‌مراتب sealed class

تست sealed class نیاز به رویکرد خاصی دارد، زیرا هر وارث یک نوع جداگانه با حالت خاص خود است. توصیه می‌شود تست‌های پارامتری‌سازی شده بنویسید که از تمام وراث sealed class عبور می‌کنند. این تضمین می‌کند که عبارات when همه گزینه‌ها از جمله موارد جدید اضافه شده هنگام گسترش سلسله‌مراتب را پوشش می‌دهند.

برای تست‌های UI، sealed class به عنوان UiState امکان بررسی نمایش هر حالت را فراهم می‌کند: Loading اسپینر را نشان می‌دهد، Content — داده‌ها، Error — پیام خطا. از آنجا که sealed class محدود است، پوشش تستی همه حالت‌ها اطمینان کامل از صحت منطق UI می‌دهد.

اشتباهات رایج هنگام کار با Sealed Class

با وجود سادگی مفهوم، توسعه‌دهندگان مرتباً هنگام طراحی سلسله‌مراتب sealed class اشتباه می‌کنند. بیایید مشکلات اصلی و راه‌های اجتناب از آنها را بررسی کنیم.

  • وراث در فایل‌های مختلف — کامپایلر اجازه اعلام sealed class را نمی‌دهد اگر وراث خارج از فایل باشند. این محدودیت when جامع را تضمین می‌کند.
  • ترکیب sealed و open — sealed class نمی‌تواند همزمان open باشد. اگر سلسله‌مراتب قابل گسترش نیاز است، از abstract class معمولی استفاده کنید، اما از جامعیت صرف‌نظر خواهید کرد.
  • تودرتویی بیش از حد — sealed class درون sealed class سلسله‌مراتب عمیقی ایجاد می‌کند که نگهداری آن دشوار است. برای سناریوهای ساده دو سطح کافی است.
  • else فراموش شده در when — اگر sealed class از کتابخانه بدون جامعیت باشد، کامپایلر در مورد شاخه از دست رفته هشدار نمی‌دهد. else را فقط آگاهانه اضافه کنید.

سوالات متداول

آیا sealed class می‌تواند متدهای انتزاعی داشته باشد؟

بله، sealed class می‌تواند حاوی متدهای انتزاعی باشد و هر وارث موظف به پیاده‌سازی آنهاست. این زمانی مناسب است که همه گزینه‌ها باید یک رابط واحد ارائه دهند اما با منطق اجرای متفاوت.

آیا sealed class در Java در دسترس است؟

در Java 17+ کلاس‌ها و رابط‌های مهر و موم شده با اصلاح‌کننده sealed ظاهر شده‌اند. Android در حال حاضر Java 17 را به صورت جزئی پشتیبانی می‌کند، اما در پروژه‌های Kotlin sealed class از Kotlin 1.0 بدون محدودیت در دسترس است.

آیا sealed class می‌تواند از sealed class دیگر ارث‌بری کند؟

بله، یک sealed class می‌تواند وارث دیگری باشد. سلسله‌مراتب sealed class محدود باقی می‌ماند: کامپایلر همه وراث را در هر سطح می‌شناسد. این امکان ساخت طبقه‌بندی‌های دقیق خطاها را فراهم می‌کند.

آیا sealed class بر عملکرد تأثیر می‌گذارد؟

Sealed class هزینه اضافی در زمان اجرا ایجاد نمی‌کند. کامپایلر عبارات when با sealed class را به جداول انتقال (tableswitch) بهینه‌سازی می‌کند که از زنجیره‌های if-else سریعتر است. عملکرد با enum یکسان است.

چگونه سلسله‌مراتب sealed class را تست کنیم؟

هر وارث sealed class به صورت جداگانه تست می‌شود. از آنجا که sealed class محدود است، می‌توان یک تست پارامتری نوشت که از همه گزینه‌ها عبور کند. این پوشش کامل شاخه‌های بلوک‌های when را فراهم می‌کند.

خلاصه

  • Sealed class — کلاسی با مجموعه ثابتی از وراث که در یک فایل اعلام شده‌اند و تحلیل جامع when را در مرحله کامپایل فراهم می‌کند.
  • هر وارث sealed class می‌تواند ساختار داده خاص خود را داشته باشد — این تفاوت اصلی با enum است که در آن همه گزینه‌ها ثابت‌های یک نوع هستند.
  • Sealed interface (Kotlin 1.5+) از وراثت چندگانه پشتیبانی می‌کند، sealed class — فقط وراثت تکی. انتخاب بستگی به نیاز به حالت مشترک دارد.
  • Sealed class — مکانیزم استاندارد برای سلسله‌مراتب نوع‌امن خطاها در Kotlin: هر نوع خرابی وارث جداگانه با فیلدهای مرتبط است.
  • الگوهای اصلی در Android: UI State، Navigation Event و Action/Intent — بر sealed class برای تضمین کامل بودن پردازش ساخته شده‌اند.
  • از وراث در فایل‌های مختلف، تودرتویی بیش از حد و ترکیب sealed با open خودداری کنید — این قرارداد سلسله‌مراتب محدود را نقض می‌کند.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید