Reified در Kotlin — چیست، نحو و کاربرد

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

reified — کلمه کلیدی در زبان Kotlin است که امکان دسترسی به نوع پارامتر generic را درون inline-function در زمان اجرا فراهم می‌کند. در generics معمولی type erasure اعمال می‌شود — اطلاعات مربوط به نوع در مرحله کامپایل پاک می‌شود، اما reified آن را حفظ می‌کند. به گفته Kotlin Documentation, 2025، reified فقط درون inline-function کار می‌کند، زیرا کامپایلر نوع واقعی را جایگزین می‌کند در مرحله inlining.

نکات اصلی

  • reified — اصلاح‌کننده پارامتر generic که اطلاعات نوع را در runtime حفظ می‌کند
  • Inline only — reified منحصراً درون inline-function کار می‌کند
  • Type erasure — مکانیزم استاندارد Java/Kotlin که انواع generic را پاک می‌کند؛ reified آن را دور می‌زند
  • بررسی is — امکان‌پذیر است: if (value is T) به جای if (value is String)
  • ایجاد نمونه — ایجاد T::class.java.newInstance() بدون ارسال Class

Reified در Kotlin چیست؟

reified — اصلاح‌کننده پارامتر generic تابع inline است که نوع را در runtime واقعی می‌کند (reify به معنای «واقعی ساختن»). بدون reified نوع T درون تابع generic قابل دسترسی نیست — کامپایلر type erasure اعمال می‌کند و تمام اطلاعات مربوط به نوع را پاک می‌کند. reified کامپایلر را مجبور می‌کند نوع مشخصی را در محل فراخوانی جایگزین کند و آن را از طریق T::class و عملگر is قابل دسترسی سازد.

به گفته Kotlin Survey by Kodee (2024)، reified type parameters در بین ده ویژگی پرتقاضای Kotlin قرار دارند — 52٪ از توسعه‌دهندگان شرکت‌کننده در نظرسنجی از آنها استفاده می‌کنند، عمدتاً برای نوشتن工厂‌های generic، ظروف DI و سریالایزرها. reified به ویژه در ترکیب با Gson، Moshi و Kotlinx Serialization محبوب است.

از نظر فنی مکانیزم ساده است: هنگام فراخوانی تابع inline با پارامتر reified، کامپایلر نوع مشخص آرگومان (Int، String، User) را می‌داند و آن را به جای T قرار می‌دهد. در بایت‌کد، پارامتر reified به Class معمولی تبدیل می‌شود که به عنوان آرگومان پنهان ارسال می‌شود.

از reified برای نوشتن توابع generic که در runtime نیاز به نوع دارند استفاده کنید — ایجاد نمونه، بررسی نوع، دریافت Class برای بازتاب یا سریالایز کردن.

مشکل type erasure در generics

Type erasure — مکانیزم Java و Kotlin که در آن اطلاعات مربوط به پارامترهای generic در زمان کامپایل پاک می‌شود. در بایت‌کد، List و List صرفاً به List تبدیل می‌شوند. این کار برای سازگاری معکوس با Java 1.4 انجام شده است، جایی که generics وجود نداشت، اما هنگام کار با انواع در runtime محدودیت ایجاد می‌کند.

kotlin
// ❌ خطا: امکان بررسی نمونه از نوع پاک شده وجود ندارد
fun <T> checkType(value: Any) {
    if (value is T) { // type erasure — T ناشناخته است
        println("نوع مطابقت دارد")
    }
}

// ✅ Solution: pass Class as parameter
fun <T> checkTypeWithClass(
    value: Any,
    clazz: Class<T>
) {
    if (clazz.isInstance(value)) {
        println("نوع مطابقت دارد")
    }
}

در مثال، checkType به دلیل type erasure کامپایل نمی‌شود — کامپایلر نمی‌داند چه نوعی را به جای T قرار دهد. در checkTypeWithClass مشکل با ارسال صریح Class حل شده است، اما این کار نیاز به boilerplate دارد: هر فراخوانی با .java یا ::class.java همراه است. reified این boilerplate را کاملاً حذف می‌کند.

نحو reified و مکانیزم کار

اصلاح‌کننده reified قبل از پارامتر generic در تابع inline قرار می‌گیرد. تابع باید حتماً inline باشد — کامپایلر باید امکان جایگزینی نوع مشخص را در مرحله inlining داشته باشد.

kotlin
inline fun <reified T> isA(value: Any): Boolean {
    return value is T
}

fun main() {
    println(isA<String>("سلام")) // true
    println(isA<Int>("سلام"))  // false
}

در زمان کامپایل، فراخوانی isA("Hello") با بررسی value is String جایگزین می‌شود. فراخوانی isA("Hello") — با value is Int. نوع به صورت تحت‌اللفظی جایگزین می‌شود که امکان استفاده از is، as، ::class و سایر عملیات غیرقابل دسترس در type erasure را فراهم می‌کند.

دکامپایل تابع reified

اگر بایت‌کد isA("Hello") را دکامپایل کنیم، IntelliJ IDEA نتیجه‌ای تقریباً به این شکل در Java نشان می‌دهد: String.class.isInstance(value). به جای پارامتر generic، کامپایلر java.lang.String.class مشخص را جایگزین کرد — هیچ بازتابی با جستجوی نوع بر اساس نام، فقط مرجع مستقیم به کلاس.

بررسی انواع با reified: is و as

رایج‌ترین کاربرد reified — بررسی نوع از طریق عملگر is. در تابع generic معمولی value is T کامپایل نمی‌شود. با reified این کار مانند یک کلاس معمولی کار می‌کند: value is String، value is List (تقریباً — با در نظر گرفتن محدودیت‌های reified برای انواع پارامتری).

kotlin
inline fun <reified T> List<Any>.filterByType(): List<T> {
    return this.filter { it is T }.map { it as T }
}

val mixed = listOf("a", 1, "b", 2)
val strings = mixed.filterByType<String>() // ["a", "b"]
val ints = mixed.filterByType<Int>()    // [1, 2]

تابع گسترشی filterByType لیست را فیلتر می‌کند و فقط عناصر نوع مشخص شده را باقی می‌گذارد. بدون reified باید filterByType(list) با پارامتر Class نوشته می‌شد. با reified فراخوانی به عنوان یک عملیات طبیعی روی لیست خوانده می‌شود که خوانایی زنجیره‌های پردازش داده را بهبود می‌بخشد.

بر اساس داده‌های Kotlin Coroutines Guide (JetBrains, 2025)، بررسی‌های نوع reified در launch و async برای ارسال نوع نتیجه coroutine استفاده می‌شود که از مشخص کردن صریح نوع در اکثر موارد جلوگیری می‌کند.

بازتاب با reified: ایجاد نمونه و دسترسی به Class

reified به T::class دسترسی می‌دهد — مرجعی به KClass که از آن می‌توان از طریق .java Class Java را دریافت کرد. این امکان ایجاد نمونه از طریق بازتاب، کار با سریالایزرها و دریافت annotation‌های کلاس را در زمان اجرا فراهم می‌کند.

kotlin
inline fun <reified T> createInstance(): T =
    T::class.java.getDeclaredConstructor().newInstance()

// استفاده
data class User(val name: String = "default")
val user = createInstance<User>()

// سریالایز کردن با Gson
inline fun <reified T> Gson.fromJson(json: String): T =
    this.fromJson(json, T::class.java)

// دریافت annotationها
inline fun <reified T> hasAnnotation<A>(): Boolean where A : Annotation =
    T::class.java.isAnnotationPresent(A::class.java)

Wrapper fromJson برای Gson — مثال کلاسیک استفاده از reified در production. به جای gson.fromJson(json, User::class.java) می‌توان gson.fromJson(json) نوشت. این به نظر بهبود کوچکی می‌رسد، اما در پروژه‌ای با صدها فراخوانی سریالایز کردن، reified به طور قابل توجهی boilerplate را کاهش می‌دهد و کد را تمیزتر می‌کند.

محدودیت‌های reified و جایگزین‌ها

reified محدودیت‌هایی دارد. اول — فقط درون توابع inline کار می‌کند. اگر تابع را نمی‌توان inline کرد (مثلاً بازگشتی است یا بیش از حد بزرگ)، reified در دسترس نیست. دوم — reified را نمی‌توان مستقیماً با توابع suspend استفاده کرد، فقط از طریق wrapper‌های inline.

سوم — reified با انواع پارامتری شده به طور کامل کار نمی‌کند. مثلاً filterByType>() ممکن است نتیجه غیرمنتظره‌ای بدهد، زیرا برای انواع پارامتری شده reified فقط نوع خام (List) را حفظ می‌کند، بدون تعیین آرگومان‌های generic. برای بررسی کامل انواع پارامتری شده، بازتاب با TypeToken مورد نیاز است.

عملیاتبا reifiedبدون reified
value is T✅ کار می‌کند❌ خطای کامپایل
T::class✅ کار می‌کند❌ خطای کامپایل
List is T⚠️ فقط raw type❌ خطا
ایجاد نمونه✅ از طریق بازتاب❌ نیاز به Class
تابع Suspend❌ فقط از طریق wrapper inline❌ قابل اجرا نیست

برای مواردی که reified در دسترس نیست، از الگو با ارسال صریح Class یا TypeToken از کتابخانه‌ها استفاده کنید (مثلاً Gson TypeToken یا Jackson TypeReference). این روش در هر تابعی کار می‌کند، اما نیاز به boilerplate دارد و کمتر راحت است.

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

چرا reified فقط با توابع inline کار می‌کند؟

کامپایلر پارامتر reified T را با نوع مشخص در زمان inlining بدنه تابع جایگزین می‌کند. اگر تابع inline نباشد، کامپایلر جایی برای جایگزینی نوع ندارد — فراخوانی تابع generic از طریق بایت‌کد واحدی انجام می‌شود که در آن T پاک شده است. Inline یک کپی جداگانه از بایت‌کد برای هر نوع-آرگومان ایجاد می‌کند.

آیا می‌توان property را reified اعلام کرد؟

خیر، reified فقط برای پارامترهای توابع قابل اعمال است. برای property‌ها از الگوی inline fun با مقدار بازگشتی یا ارسال صریح Class از طریق سازنده استفاده می‌شود. Extension properties نیز از reified پشتیبانی نمی‌کنند.

reified با انواع nullable چگونه کار می‌کند؟

reified از انواع nullable پشتیبانی می‌کند: reified T : Any (non-null) و صرفاً reified T (می‌تواند nullable باشد). برای انواع nullable، T::class کلاس نسخه non-null را برمی‌گرداند (String::class برای String?). بررسی value is T null را در نظر می‌گیرد: اگر T = String?، آنگاه null is T = true.

آیا reified سربار دارد؟

حداقل. reified از بازتاب استفاده نمی‌کند — کامپایلر نوع مشخص را در مرحله inlining جایگزین می‌کند. در بایت‌کد این یک مرجع مستقیم به کلاس است (ldc + checkcast/invokevirtual). در مقایسه با ارسال دستی Class سرباری وجود ندارد — هر دو گزینه بایت‌کد یکسانی تولید می‌کنند.

آیا می‌توان از reified در توسعه Android استفاده کرد؟

بله، reified به طور فعال در Android استفاده می‌شود. Bundle.getParcelable()، Intent.getSerializableExtra()، viewModels() از Android KTX — همه این توابع از reified استفاده می‌کنند تا از ارسال صریح Class جلوگیری کنند. بر اساس داده‌های Google Android Docs (2025)، reified برای API generic که در runtime نیاز به نوع دارند توصیه می‌شود.

خلاصه

  • reified — اصلاح‌کننده پارامتر generic تابع inline که نوع را در runtime حفظ می‌کند
  • Type erasure — مکانیزم استاندارد پاک کردن انواع؛ reified با inlining آن را دور می‌زند
  • is/as — بررسی و تبدیل انواع با reified مانند کلاس‌های معمولی کار می‌کند
  • مرجع کلاس — T::class و T::class.java برای بازتاب و سریالایز کردن در دسترس هستند
  • فقط inline — reified بدون تابع inline به دلیل مکانیزم جایگزینی نوع ممکن نیست
  • انواع پارامتری شده — reified آرگومان‌های generic را حفظ نمی‌کند (فقط raw type)
  • کاربرد — سریالایز کردن Gson/Moshi، ظروف DI، بررسی انواع در مجموعه‌ها، Android KTX

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

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

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

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