Reflection در توسعه اپلیکیشن‌ها — چیست، مکانیزم‌های انعکاس و نحوه استفاده از آن

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

Reflection (انعکاس) — مکانیزم زمان اجرا است که به کد امکان می‌دهد ساختار خود را بررسی کند: دریافت کلاس‌ها، متدها، فیلدها و annotation ها بدون دانستن انواع در مرحله کامپایل. این ابزار در پایه بسیاری از فریم‌ورک‌های موبایل قرار دارد — سریالایز JSON (Gson, Moshi)، تزریق وابستگی (Dagger, Koin) و اجراکننده‌های تست (JUnit, XCTest). بر اساس آموزش رسمی Reflection جاوا، Oracle، 2024، reflection عنصر اجباری پلتفرم Java است که توسط همه کتابخانه‌های بزرگ استفاده می‌شود.

نکات اصلی

  • Reflection — دسترسی به فراداده‌های کلاس‌ها، متدها و فیلدها در زمان اجرای برنامه.
  • Java Reflection API کلاس‌های Class، Method، Field و Constructor را برای تحلیل پویا فراهم می‌کند.
  • Reflection در Kotlin از KClass و KFunction استفاده می‌کند که با coroutine ها و سریالایز ادغام شده‌اند.
  • Objective-C Runtime — شکلی از reflection از طریق class_copyMethodList و objc_getClass.
  • عملکرد reflection به دلیل نبود بهینه‌سازی‌های JIT، ۱۰–۱۰۰ برابر کمتر از فراخوانی‌های مستقیم است.

Reflection چیست؟

Reflection — توانایی برنامه برای مشاهده و تغییر ساختار و رفتار خود در زمان اجرا. در زبان‌های شیءگرا این به معنای دریافت اشیاء Class، Method، Field و Constructor است که عناصر برنامه را به‌صورت داده‌های قابل خواندن و فراخوانی نمایش می‌دهند.

اصطلاح «reflection» در سال ۱۹۸۲ در جامعه هوش مصنوعی (برایان کانتول اسمیت) معرفی و در زبان Smalltalk پیاده‌سازی شد. در توسعه موبایل، reflection اولین بار در Java ME و Objective-C (۱۹۸۶، NextStep) ظاهر شد. امروزه هر پلتفرم موبایل بزرگ API reflection خود را دارد: Java/Kotlin برای Android، Objective-C Runtime برای iOS، Swift Mirror API برای Swift.

مکانیزم reflection بر فراداده‌هایی استوار است که کامپایلر در بایت‌کد یا فایل باینری ذخیره می‌کند. Android اطلاعات کامل درباره کلاس‌ها را در فایل‌های DEX ذخیره می‌کند، iOS — در بخش __objc_classlist در Mach-O. Runtime این فراداده‌ها را در حافظه بارگذاری می‌کند و API برای پیمایش آن‌ها فراهم می‌کند.

Reflection در Java و Kotlin چگونه کار می‌کند

Java Reflection API حول کلاس java.lang.Class ساخته می‌شود. هر شیء در Java را می‌توان از طریق .getClass() یا Class.forName() به Class تبدیل کرد. از Class همه متدها، فیلدها، سازنده‌ها، annotation ها و کلاس‌های والد استخراج می‌شوند. Kotlin reflection جاوا را به ارث می‌برد و KClass، KFunction، KProperty خود را از بسته kotlin.reflect اضافه می‌کند.

kotlin
import kotlin.reflect.full.declaredMemberFunctions

data class User(
    val name: String,
    val email: String
)

fun inspectClass() {
    val kClass = User::class
    val properties = kClass.declaredMemberProperties
    val functions = kClass.declaredMemberFunctions

    properties.forEach { prop ->
        println("ویژگی: ${prop.name}، نوع: ${prop.returnType}")
    }
}

در این مثال، KClass فراداده‌های data class User را فراهم می‌کند. declaredMemberProperties فهرست ویژگی‌ها را همراه با نوع‌ها و getter های آن‌ها برمی‌گرداند. reflection در Kotlin به‌طور نزدیک با coroutine ها ادغام شده است: KFunction از اصلاح‌کننده suspend پشتیبانی می‌کند که امکان فراخوانی متدهای ناهمگام را از طریق reflection فراهم می‌کند.

Java Reflection: Class، Method، Field

reflection در Java با Class<?>، Method.setAccessible() و Field.get() کار می‌کند. setAccessible(true) بررسی دسترسی Java language access control را برای عناصر private غیرفعال می‌کند. این مکانیزم قدرتمند اما خطرناک است: در Android از API 28 فراخوانی setAccessible روی متدهای مخفی سیستم می‌تواند InaccessibleObjectException ایجاد کند.

java
// reflection جاوا: فراخوانی متد خصوصی
Class clazz = Class.forName("com.example.MyClass");
Object instance = clazz.getDeclaredConstructor().newInstance();
Method method = clazz.getDeclaredMethod("privateMethod", String.class);
method.setAccessible(true);
method.invoke(instance, "reflection test");

کد Class.forName() را نشان می‌دهد — بارگذاری پویای کلاس بر اساس نام رشته‌ای. این اساس معماری‌های پلاگین است: کلاس ممکن است در مرحله کامپایل ناشناخته باشد، اما در زمان اجرا از طریق reflection بارگذاری و اجرا شود. getDeclaredMethod(«privateMethod»، ...) متد را بر اساس نام و نوع پارامترها پیدا می‌کند و invoke آن را اجرا می‌کند.

Objective-C Runtime: مدل جایگزین انعکاس

Objective-C runtime توابع class_copyMethodList، class_copyPropertyList، objc_getAssociatedObject را فراهم می‌کند. برخلاف Java، Objective-C متدهای خصوصی را به‌طور پیش‌فرض پنهان نمی‌کند — runtime همه متدهای کلاس را می‌بیند. این توضیح می‌دهد چرا method swizzling بدون setAccessible کار می‌کند: runtime در سطح فراداده‌ها کپسوله‌سازی ندارد.

کاربرد Reflection در توسعه موبایل

Reflection در کتابخانه‌های کلیدی توسعه موبایل استفاده می‌شود. سریالایز JSON (Gson, Moshi, Kotlinx.serialization) ویژگی‌های شیء را از طریق reflection دریافت می‌کند و آن‌ها را با کلیدهای JSON مطابقت می‌دهد. تزریق وابستگی (Dagger, Koin, Swinject) سازنده‌ها و فیلدها را برای تزریق خودکار وابستگی‌ها تحلیل می‌کند. کتابخانه‌های ORM (Room, Realm) از reflection برای نگاشت کلاس‌ها به جدول‌های پایگاه داده استفاده می‌کنند.

  • سریالایز — Gson فیلدهای اعلام‌شده شیء را از طریق Field.get() می‌خواند و بر اساس annotation های @SerializedName JSON می‌سازد.
  • تزریق وابستگی — Dagger کد را از طریق annotation processing تولید می‌کند، Koin برای حل وابستگی در زمان اجرا از reflection کاتلین استفاده می‌کند.
  • تست — JUnit متدهای دارای @Test را از طریق reflection پیدا و فراخوانی می‌کند؛ Mockito از طریق dynamic proxy mock می‌سازد.
  • پایگاه داده — Room فیلدهای Entity را از طریق Class.getDeclaredFields() در مرحله کامپایل (با KAPT/KSP) بررسی می‌کند.
  • تحلیل و پایش — Firebase Crashlytics stack trace را از طریق Throwable.getStackTrace() دریافت می‌کند که بر reflection استوار است.

هر یک از این کاربردها دقیقاً در زمان اجرا کار می‌کند — کد از قبل نمی‌داند با چه کلاس‌هایی مواجه خواهد شد. Reflection مکانیزمی جهانی برای عبور از این ناشناختگی به بهای عملکرد و امنیت فراهم می‌کند.

عملکرد Reflection: بهای دسترسی پویا

Reflection ۱۰–۱۰۰ برابر کندتر از فراخوانی مستقیم متدها است. دلیل — نبود بهینه‌سازی‌های JIT (devirtualization، inlining)، بررسی انواع در هر فراخوانی و بسته‌بندی پارامترها در Object[]/varargs. ART در Android 14 نمی‌تواند فراخوانی‌های reflection را به‌صورت inline بهینه کند، زیرا متد هدف تا لحظه اجرا ناشناخته است.

عملیاتفراخوانی مستقیماز طریق Reflectionکاهش سرعت
فراخوانی متد بدون پارامتر~3 ns~120 ns40x
خواندن فیلد int~1 ns~85 ns85x
فراخوانی متد با 2 پارامتر~4 ns~250 ns62x
ایجاد نمونه از طریق سازنده~5 ns~180 ns36x
تعیین کلاس بر اساس رشته~800 ns

داده‌ها بر روی Google Pixel 8 (Android 14، ART) به دست آمده‌اند. عملکرد reflection با هر نسخه Android بهبود می‌یابد: در Android 9 فراخوانی از طریق Method.invoke() ۱۵۰ برابر کندتر از مستقیم بود، در Android 14 — ۴۰ برابر. ART از مکانیزم‌های داخلی method handle برای بهینه‌سازی استفاده می‌کند.

برای بخش‌های حساس به عملکرد، توسعه‌دهندگان reflection را با code generation جایگزین می‌کنند: Dagger به‌جای جستجوی زمان اجرا از annotation processing استفاده می‌کند، Kotlinx.serialization سریالایزرها را از طریق KSP تولید می‌کند، Moshi برای codegen در مرحله کامپایل @JsonClass(generateAdapter = true) را تطبیق می‌دهد.

جایگزین‌های Reflection: annotation ها و code generation

Annotation processing (KAPT، KSP) و code generation — جایگزین‌های اصلی reflection در توسعه موبایل هستند. آن‌ها تحلیل فراداده‌ها را از زمان اجرا به مرحله کامپایل منتقل می‌کنند: کد قبل از راه‌اندازی برنامه تولید می‌شود که سربار reflection را حذف و عملکرد را بهبود می‌دهد.

kotlin
// KSP: code generation به جای reflection
@Serializable
data class Config(
    val apiUrl: String,
    val timeout: Int
)

// KSP ConfigSerializer را بدون reflection تولید می‌کند
fun loadConfig(json: String): Config {
    return Config.serializer().decodeFromString(json)
}

در این مثال، @Serializable annotation سریالایز کاتلین است. KSP (Kotlin Symbol Processing) کد منبع را در مرحله کامپایل تحلیل می‌کند، همه کلاس‌های @Serializable را پیدا و سریالایزرها را تولید می‌کند. در زمان اجرای برنامه reflection استفاده نمی‌شود — سریالایزر قبلاً به کد ماشین کامپایل شده است.

مقایسه رویکردها

Code generation عملکرد بهتر، امنیت نوع و اندازه باینری کوچک‌تر فراهم می‌کند (dead code elimination وابستگی‌های بلااستفاده reflection را حذف می‌کند). Reflection برای کارهایی که انواع در مرحله کامپایل ناشناخته‌اند ضروری باقی می‌ماند: بارگذاری پویای پلاگین‌ها، پروکسی زمان اجرا، ابزاربندی تست‌ها. بر اساس داده‌های Kotlin، Kotlinx.serialization با KSP ۳–۵ برابر سریع‌تر از Gson مبتنی بر reflection است.

محدودیت‌های Reflection در Android و iOS

Reflection در پلتفرم‌های موبایل محدودیت‌های امنیتی و عملکردی دارد. Android از API 28 (Pie) setAccessible را برای رابط‌های غیر SDK محدود می‌کند — تلاش برای باز کردن متد مخفی سیستم به استثنا یا هشدار منجر می‌شود. iOS با Swift از reflection به معنای کلاسیک پشتیبانی نمی‌کند: Swift Mirror API فقط خواندن ویژگی‌ها (name, value) را بدون تغییر یا فراخوانی متدها فراهم می‌کند.

Google Play برنامه‌هایی را که از reflection برای دور زدن محدودیت‌های پلتفرم استفاده می‌کنند رد می‌کند: جایگزینی سرویس‌های سیستم، تغییر سیاست‌های SELinux، خواندن مجوزهای محافظت‌شده. Apple نیز برنامه‌هایی را که از طریق reflection API خصوصی فراخوانی می‌کنند مسدود می‌کند — بررسی App Review فایل باینری را برای امضاهای رشته‌ای objc_msgSend با سلکتورهای خصوصی شناخته‌شده اسکن می‌کند.

ProGuard/R8 — محدودیت دیگری است. مبهم‌سازی و فشرده‌سازی کد، کلاس‌ها و متدها را به نام‌های کوتاه (a, b, c) تغییر نام می‌دهد. اگر کد از Class.forName(«com.example.MyClass») استفاده کند، پس از مبهم‌سازی خراب می‌شود. راه‌حل — قوانین keep در proguard-rules.pro:

groovy
// قوانین keep برای reflection در ProGuard
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod

قوانین -keep به R8 اعلام می‌کنند که کلاس‌های استفاده‌شده از طریق reflection را تغییر نام ندهد. بدون این قوانین، برنامه مبهم‌سازی‌شده با ClassNotFoundException سقوط می‌کند — runtime نمی‌تواند کلاس را با نام رشته‌ای تغییر یافته پیدا کند.

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

آیا Reflection برای عملکرد برنامه مضر است؟

بله، reflection ۱۰–۱۰۰ برابر کندتر از فراخوانی مستقیم است. دلایل اصلی: نبود بهینه‌سازی‌های JIT (inlining، devirtualization)، بسته‌بندی پارامترها و بررسی انواع در هر فراخوانی. برای کد production توصیه می‌شود reflection را با code generation از طریق KSP یا annotation processing جایگزین کنید.

reflection جاوا چه تفاوتی با reflection کاتلین دارد؟

reflection جاوا از طریق Class، Method، Field کار می‌کند و برای اعضای private به setAccessible نیاز دارد. reflection کاتلین از KClass، KFunction، KProperty استفاده می‌کند و از sealed class، data class، coroutine ها (توابع suspend) و null-safety پشتیبانی می‌کند. reflection کاتلین بر reflection جاوا استوار است اما API type-safe اضافه می‌کند.

چگونه هنگام استفاده از Reflection از مشکلات مبهم‌سازی جلوگیری کنیم؟

برای کلاس‌ها، متدها و فیلدهای استفاده‌شده از طریق reflection، قوانین keep مربوط به ProGuard/R8 را اضافه کنید. برای هر Class.forName()، getDeclaredMethod()، getDeclaredField() باید دستورالعمل -keep مربوطه وجود داشته باشد. ابزارهایی مانند GreenDAO و Room به‌طور خودکار قوانین keep تولید می‌کنند.

آیا Reflection در Swift وجود دارد؟

Swift به معنای کامل reflection ندارد. Mirror API (Swift 2+) امکان خواندن ویژگی‌های ساختار یا کلاس را فراهم می‌کند: نام، مقدار، نوع. فراخوانی متدها، تغییر فیلدها و ایجاد نمونه بر اساس نوع ممکن نیست. برای این کار هنگام ارث‌بری از NSObject با @objc dynamic از Objective-C Runtime استفاده می‌شود.

کدام کتابخانه‌ها در Android از Reflection استفاده می‌کنند؟

Gson (سریالایز JSON)، Retrofit (ایجاد implementation رابط‌ها از طریق dynamic proxy)، Mockito (ایجاد mock)، Koin (تزریق وابستگی)، Room (بررسی Entity در مرحله کامپایل از طریق KAPT)، Firebase Crashlytics (تحلیل stack trace). بیشتر کتابخانه‌ها به code generation با KSP/KAPT روی می‌آورند.

جمع‌بندی

  • Reflection — مکانیزم زمان اجرا برای دسترسی به فراداده‌های کلاس‌ها، متدها و فیلدها.
  • reflection جاوا از Class، Method، Field استفاده می‌کند؛ Kotlin — از KClass، KFunction، KProperty با ادغام coroutine ها.
  • Objective-C Runtime class_copyMethodList و objc_getClass را بدون محدودیت دسترسی فراهم می‌کند.
  • reflection کندتر — به دلیل نبود بهینه‌سازی‌های JIT، ۱۰–۱۰۰ برابر کندتر از فراخوانی مستقیم است.
  • جایگزین‌ها — code generation (KSP، KAPT) و annotation processing — سربار reflection را حذف می‌کنند.
  • ProGuard/R8 برای کلاس‌های استفاده‌شده از طریق Class.forName() و getDeclaredMethod() به قوانین keep نیاز دارد.
  • Reflection برای بارگذاری پویای پلاگین‌ها، DI و فریم‌ورک‌های تست که انواع در مرحله کامپایل ناشناخته‌اند، بی‌جایگزین است.

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

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

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

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