Reflection (refleksiya) — runtime mexanizmi olub, koda öz strukturunu tədqiq etməyə imkan verir: kompilyasiya mərhələsində tipləri bilmədən sinifləri, metodları, sahələri və annotasiyaları əldə etməyə imkan verir. Bu alət bir çox mobil freymvorkların — JSON serializasiyasının (Gson, Moshi), dependency injection-un (Dagger, Koin) və test runner-larının (JUnit, XCTest) əsasını təşkil edir. Oracle Java Reflection Tutorial, 2024 məlumatına görə, reflection bütün böyük kitabxanalar tərəfindən istifadə olunan Java platformasının məcburi elementidir.
Əsas məqamlar
Reflection — proqramın icra zamanı öz strukturunu və davranışını müşahidə etmə və dəyişdirmə qabiliyyəti. Obyekt-yönümlü dillərdə bu, proqramın elementlərini oxunma və çağırış üçün əlçatan verilənlər kimi təqdim edən Class, Method, Field və Constructor obyektlərinin əldə edilməsi deməkdir.
„reflection” termini 1982-ci ildə süni intellekt icmasında (Brian Cantwell Smith) təqdim edilmiş və Smalltalk dilində reallaşdırılmışdır. Mobil inkişafda reflection ilk dəfə Java ME və Objective-C (1986, NextStep) dillərində meydana çıxmışdır. Bu gün hər böyük mobil platformanın öz reflection API-si var: Android üçün Java/Kotlin, iOS üçün Objective-C Runtime, Swift üçün Swift Mirror API.
Reflection mexanizmi kompilyatorun bayt-kodda və ya binar faylda saxladığı metadata məlumatlarına əsaslanır. Android siniflər haqqında tam məlumatı DEX-fayllarında saxlayır, iOS — Mach-O-nun __objc_classlist seqmentində. Runtime bu metadata məlumatlarını yaddaşa yükləyir və onların keçilməsi üçün API təqdim edir.
Java Reflection API java.lang.Class sinifi ətrafında qurulur. Java-da istənilən obyekti .getClass() və ya Class.forName() vasitəsilə Class-a çevirmək olar. Class-dan bütün metodlar, sahələr, konstruktorlar, annotasiyalar və super-siniflər çıxarılır. Kotlin Java reflection-u miras alır və kotlin.reflect paketindən öz KClass, KFunction, KProperty-lərini əlavə edir.
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("Xüsusiyyət: ${prop.name}, tip: ${prop.returnType}")
}
}
Bu nümunədə KClass data class User-in metadata məlumatlarını təqdim edir. declaredMemberProperties tipləri və getter-ləri ilə birlikdə xassələrin siyahısını qaytarır. Kotlin reflection korutinlərlə sıx inteqrasiya olunub: KFunction suspend-mofifikatoru dəstəkləyir və bu, asinxron metodları reflection vasitəsilə çağırmağa imkan verir.
Java reflection Class<?>, Method.setAccessible() və Field.get() ilə işləyir. setAccessible(true) private-elementlər üçün Java language access control giriş yoxlamasını söndürür. Bu güclü, lakin təhlükəli mexanizmdir: Android-də API 28-dən başlayaraq gizli sistem metodlarında setAccessible çağırışı InaccessibleObjectException-a səbəb ola bilər.
// Java reflection: privat metodun çağırılması
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");
Kod Class.forName() metodunu nümayiş etdirir — sinfin sətir adı ilə dinamik yüklənməsi. Bu, plagin arxitekturalarının əsasıdır: sinif kompilyasiya mərhələsində məlum olmaya bilər, lakin runtime-da reflection vasitəsilə yüklənib icra olunur. getDeclaredMethod(„privateMethod”, ...) metodu ad və parametr tiplərinə görə tapır, invoke isə onu icra edir.
Objective-C runtime class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject funksiyalarını təqdim edir. Java-dan fərqli olaraq, Objective-C private metodları standart olaraq gizlətmir — runtime sinfin bütün metodlarını görür. Bu, method swizzling-in nə üçün setAccessible olmadan işlədiyini izah edir: runtime metadata səviyyəsində inkapsulyasiyaya malik deyil.
Reflection mobil inkişafın əsas kitabxanalarında istifadə olunur. JSON serializasiyası (Gson, Moshi, Kotlinx.serialization) obyektin xassələrini reflection vasitəsilə əldə edir və onları JSON açarları ilə uyğunlaşdırır. Dependency injection (Dagger, Koin, Swinject) asılılıqların avtomatik yerləşdirilməsi üçün konstruktorları və sahələri analiz edir. ORM kitabxanaları (Room, Realm) sinifləri verilənlər bazası cədvəllərinə uyğunlaşdırmaq üçün reflection-dan istifadə edir.
Bu tətbiqlərin hər biri məhz runtime-da işləyir — kod əvvəlcədən hansı siniflərlə qarşılaşacağını bilmir. Reflection bu qeyri-müəyyənliyi performans və təhlükəsizlik bahasına keçmək üçün universal mexanizm təqdim edir.
Reflection metodların birbaşa çağırılmasından 10–100 dəfə yavaşdır. Səbəb — JIT-optimizasiyaların (devirtualization, inlining) olmaması, hər çağırışda tiplərin yoxlanılması və parametrlərin Object[]/varargs-a yığılmasıdır. Android 14-də ART reflection-çağırışlarını inline-optimizasiya edə bilmir, çünki hədəf metod icra anına qədər məlum deyil.
| Əməliyyat | Birbaşa çağırış | Reflection vasitəsilə | Yavaşlama |
|---|---|---|---|
| Parametrsiz metod çağırışı | ~3 ns | ~120 ns | 40x |
| int sahəsinin oxunması | ~1 ns | ~85 ns | 85x |
| 2 parametrli metod çağırışı | ~4 ns | ~250 ns | 62x |
| Konstruktor vasitəsilə nüsxə yaradılması | ~5 ns | ~180 ns | 36x |
| Sinfin sətir üzrə müəyyən edilməsi | — | ~800 ns | — |
Məlumatlar Google Pixel 8 cihazında (Android 14, ART) əldə edilib. Reflection-un performansı Android-in hər versiyası ilə yaxşılaşır: Android 9-da Method.invoke() vasitəsilə çağırış birbaşa çağırışdan 150 dəfə, Android 14-də isə 40 dəfə yavaş idi. ART optimizasiya üçün method handle daxili mexanizmlərindən istifadə edir.
Performans baxımından kritik hissələrdə tərtibatçılar reflection-u kod-generasiya ilə əvəz edirlər: Dagger runtime-axtarış əvəzinə annotation processing istifadə edir, Kotlinx.serialization KSP vasitəsilə serializatorlar generasiya edir, Moshi compile-time codegen üçün @JsonClass(generateAdapter = true) tətbiq edir.
Annotation processing (KAPT, KSP) və code generation — mobil inkişafda reflection-un əsas alternativləridir. Onlar metadata analizini runtime-dan compile time-a köçürür: kod tətbiqin işə salınmasından əvvəl generasiya olunur, bu da reflection overhead-i aradan qaldırır və performansı yaxşılaşdırır.
// KSP: reflection əvəzinə code generation
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP ConfigSerializer-i reflection olmadan generasiya edir
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
Bu nümunədə @Serializable — Kotlinx.serialization annotasiyasıdır. KSP (Kotlin Symbol Processing) kompilyasiya mərhələsində mənbə kodu analiz edir, bütün @Serializable siniflərini tapır və serializatorlar generasiya edir. Tətbiqin icrası zamanı reflection istifadə olunmur — serializator artıq maşın koduna kompilyasiya olunub.
Code generation daha yaxşı performans, tip təhlükəsizliyi və daha kiçik binar fayl ölçüsü təmin edir (dead code elimination istifadə olunmayan reflection asılılıqlarını silir). Reflection tiplərin kompilyasiya mərhələsində məlum olmadığı hallar üçün zəruri olaraq qalır: plaginlərin dinamik yüklənməsi, runtime-proksi, testlərin instrumentasiyası. Kotlin-in məlumatına görə, KSP ilə Kotlinx.serialization reflection-a əsaslanan Gson-dan 3–5 dəfə sürətlidir.
Reflection mobil platformalarda təhlükəsizlik və performans məhdudiyyətlərinə malikdir. Android API 28-dən (Pie) başlayaraq non-SDK interfeyslər üçün setAccessible-ı məhdudlaşdırır — gizli sistem metodunu açmaq cəhdi istisnaya və ya xəbərdarlığa səbəb olur. iOS Swift ilə reflection-u klassik mənada dəstəkləmir: Swift Mirror API yalnız xassələrin oxunmasını (name, value) təmin edir, metodların dəyişdirilməsi və ya çağırılması olmadan.
Google Play platforma məhdudiyyətlərini keçmək üçün reflection-dan istifadə edən tətbiqləri rədd edir: sistem xidmətlərinin əvəzlənməsi, SELinux siyasətlərinin dəyişdirilməsi, qorunan icazələrin oxunması. Apple da reflection vasitəsilə private API çağıran tətbiqləri bloklayır — App Review yoxlaması binari faylı məlum private seçiciləri olan objc_msgSend sətir imzaları üçün skan edir.
ProGuard/R8 — daha bir məhdudiyyət. Kodun obfuskasiyası və minifikasiyası sinifləri və metodları qısa adlarla (a, b, c) adlandırır. Kod Class.forName(„com.example.MyClass”) istifadə edirsə, obfuskasiyadan sonra sınacaq. Həll — proguard-rules.pro faylında keep-qaydaları:
// Reflection üçün ProGuard keep-qaydaları
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
-keep qaydaları R8-ə reflection vasitəsilə istifadə olunan sinifləri adlandırmamağı bildirir. Bu qaydalar olmadan obfuskasiya olunmuş tətbiq ClassNotFoundException ilə çökəcək — runtime sinfi dəyişmiş sətir adı ilə tapa bilməyəcək.
Tez-tez verilən suallar
Bəli, reflection birbaşa çağırışdan 10–100 dəfə yavaşdır. Əsas səbəblər: JIT-optimizasiyaların (inlining, devirtualization) olmaması, parametrlərin yığılması və hər çağırışda tiplərin yoxlanılması. Production-kod üçün reflection-u KSP və ya annotation processing vasitəsilə code generation ilə əvəz etmək tövsiyə olunur.
Java reflection Class, Method, Field vasitəsilə işləyir və private üzvlər üçün setAccessible tələb edir. Kotlin reflection KClass, KFunction, KProperty istifadə edir və sealed class, data class, korutinlər (suspend-funksiyalar) və null-safety dəstəkləyir. Kotlin reflection Java reflection-na əsaslanır, lakin type-safe API əlavə edir.
Reflection vasitəsilə istifadə olunan siniflər, metodlar və sahələr üçün ProGuard/R8 keep-qaydaları əlavə edin. Hər Class.forName(), getDeclaredMethod(), getDeclaredField() üçün müvafiq -keep direktivi olmalıdır. GreenDAO və Room kimi alətlər keep-qaydalarını avtomatik generasiya edir.
Swift-də reflection tam mənada yoxdur. Mirror API (Swift 2+) strukturun və ya sinfin xassələrini oxumağa imkan verir: ad, dəyər, tip. Metodların çağırılması, sahələrin dəyişdirilməsi və tip üzrə nüsxələrin yaradılması mümkün deyil. Bunun üçün @objc dynamic ilə NSObject-dən miras alarkən Objective-C Runtime istifadə olunur.
Gson (JSON-serializasiya), Retrofit (dynamic proxy vasitəsilə interfeys implementation-larının yaradılması), Mockito (mock-ların yaradılması), Koin (dependency injection), Room (Entity yoxlanışı kompilyasiya mərhələsində KAPT ilə), Firebase Crashlytics (stack trace analizi). Əksər kitabxanalar KSP/KAPT ilə code generation-a keçir.
Nəticələr
Açar təslim mobil tətbiq hazırlayacağıq
IT Sectr 2017-ci ildən startaplar və bizneslər üçün iOS və Android tətbiqləri yaradır. Sizə məsləhət verəcəyik və ən yaxşı həlli təklif edəcəyik.
Həm də oxuyun