Reflection (repleksyon) — isang runtime na mekanismo na nagpapahintulot sa code na siyasatin ang sariling istraktura: kumuha ng mga klase, pamamaraan, field at anotasyon nang hindi nalalaman ang mga tipo sa yugto ng kompilasyon. Ang tool na ito ang pundasyon ng maraming mobile framework — JSON serialization (Gson, Moshi), dependency injection (Dagger, Koin) at mga test runner (JUnit, XCTest). Ayon sa Oracle Java Reflection Tutorial, 2024, ang reflection ay isang sapilitang elemento ng platform ng Java na ginagamit ng lahat ng malalaking library.
Mahahalagang punto
Reflection — ang kakayahan ng programa na obserbahan at baguhin ang sariling istraktura at pag-uugali habang tumatakbo. Sa mga object-oriented na wika, nangangahulugan ito ng pagkuha ng mga bagay na Class, Method, Field at Constructor, na kumakatawan sa mga elemento ng programa bilang data na magagamit para basahin at tawagin.
Ang terminong „reflection” ay ipinakilala sa komunidad ng artificial intelligence noong 1982 (Brian Cantwell Smith) at ipinatupad sa wikang Smalltalk. Sa mobile development, unang lumitaw ang reflection sa Java ME at Objective-C (1986, NextStep). Ngayon bawat malaking mobile platform ay may sariling reflection API: Java/Kotlin para sa Android, Objective-C Runtime para sa iOS, Swift Mirror API para sa Swift.
Ang mekanismo ng reflection ay batay sa metadata na iniimbak ng compiler sa bytecode o binary file. Ang Android ay nag-iimbak ng kumpletong impormasyon tungkol sa mga klase sa mga DEX file, iOS — sa __objc_classlist segment sa Mach-O. Niloload ng Runtime ang metadata na ito sa memory at nagbibigay ng API para sa pagtawid sa mga ito.
Ang Java Reflection API ay binuo sa paligid ng klase na java.lang.Class. Anumang bagay sa Java ay maaaring i-convert sa Class sa pamamagitan ng .getClass() o Class.forName(). Mula sa Class ay kinukuha ang lahat ng pamamaraan, field, constructor, anotasyon at superclass. Ang Kotlin ay nagmamana ng Java reflection at nagdaragdag ng sariling KClass, KFunction, KProperty mula sa package na kotlin.reflect.
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("Property: ${prop.name}, tipo: ${prop.returnType}")
}
}
Sa halimbawang ito, ang KClass ay nagbibigay ng metadata ng data class User. Ang declaredMemberProperties ay nagbabalik ng listahan ng mga property kasama ang kanilang mga tipo at getter. Ang Kotlin reflection ay malapit na isinama sa mga coroutine: sinusuportahan ng KFunction ang suspend-modifier, na nagpapahintulot sa pagtawag ng mga asynchronous na pamamaraan sa pamamagitan ng reflection.
Ang Java reflection ay gumagana sa Class<?>, Method.setAccessible() at Field.get(). Ang setAccessible(true) ay nag-o-off sa pagsusuri ng access na Java language access control para sa mga private na elemento. Ito ay isang makapangyarihan ngunit mapanganib na mekanismo: sa Android, mula sa API 28, ang pagtawag ng setAccessible sa mga nakatagong sistema na pamamaraan ay maaaring magdulot ng InaccessibleObjectException.
// Java reflection: pagtawag ng pribadong pamamaraan
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");
Ang code ay nagpapakita ng Class.forName() — dynamic na pag-load ng klase batay sa string na pangalan. Ito ang batayan ng mga plugin architecture: ang klase ay maaaring hindi kilala sa yugto ng kompilasyon, ngunit niload at pinatatakbo sa pamamagitan ng reflection sa runtime. Ang getDeclaredMethod(„privateMethod”, ...) ay nakakahanap ng pamamaraan batay sa pangalan at mga tipo ng parameter, at ang invoke ay nagpapatakbo nito.
Ang Objective-C runtime ay nagbibigay ng mga function na class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject. Hindi tulad ng Java, ang Objective-C ay hindi nagtatago ng mga private na pamamaraan bilang default — nakikita ng runtime ang lahat ng pamamaraan ng klase. Ipinapaliwanag nito kung bakit gumagana ang method swizzling nang walang setAccessible: ang runtime ay walang encapsulation sa antas ng metadata.
Ang Reflection ay ginagamit sa mga pangunahing library ng mobile development. Ang JSON serialization (Gson, Moshi, Kotlinx.serialization) ay kumukuha ng mga property ng object sa pamamagitan ng reflection at inilalapat ang mga ito sa mga JSON key. Ang dependency injection (Dagger, Koin, Swinject) ay nagsusuri ng mga constructor at field para sa awtomatikong pag-inject ng mga dependency. Ang mga ORM library (Room, Realm) ay gumagamit ng reflection para sa pagma-map ng mga klase sa mga table ng database.
Ang bawat isa sa mga paglalapat na ito ay gumagana mismo sa runtime — hindi alam ng code nang maaga kung anong mga klase ang makakaharap nito. Ang Reflection ay nagbibigay ng unibersal na mekanismo para malampasan ang hindi alam na ito kapalit ng performance at seguridad.
Ang Reflection ay 10–100 beses na mas mabagal kaysa sa direktang pagtawag ng mga pamamaraan. Ang dahilan — kawalan ng JIT optimizations (devirtualization, inlining), pagsusuri ng mga tipo sa bawat tawag at pag-iimpake ng mga parameter sa Object[]/varargs. Ang ART sa Android 14 ay hindi makapag-optimize ng inline ng mga reflection na tawag, dahil ang target na pamamaraan ay hindi kilala hanggang sa sandali ng pagpapatupad.
| Operasyon | Direktang tawag | Sa pamamagitan ng Reflection | Pagbagal |
|---|---|---|---|
| Pagtawag ng pamamaraan nang walang parameter | ~3 ns | ~120 ns | 40x |
| Pagbasa ng int field | ~1 ns | ~85 ns | 85x |
| Pagtawag ng pamamaraan na may 2 parameter | ~4 ns | ~250 ns | 62x |
| Paglikha ng instance sa pamamagitan ng constructor | ~5 ns | ~180 ns | 36x |
| Pagtukoy ng klase batay sa string | — | ~800 ns | — |
Ang data ay nakuha sa Google Pixel 8 (Android 14, ART). Ang performance ng reflection ay bumubuti sa bawat bersyon ng Android: sa Android 9, ang tawag sa pamamagitan ng Method.invoke() ay 150 beses na mas mabagal kaysa sa direktang, sa Android 14 — 40 beses. Gumagamit ang ART ng built-in na method handle na mekanismo para sa pag-optimize.
Para sa mga bahaging kritikal sa performance, pinapalitan ng mga developer ang reflection ng code generation: ang Dagger ay gumagamit ng annotation processing sa halip na runtime na paghahanap, ang Kotlinx.serialization ay bumubuo ng mga serializer sa pamamagitan ng KSP, ang Moshi ay iniaangkop ang @JsonClass(generateAdapter = true) para sa codegen sa yugto ng kompilasyon.
Ang annotation processing (KAPT, KSP) at code generation — ang mga pangunahing alternatibo sa reflection sa mobile development. Inililipat nila ang pagsusuri ng metadata mula sa runtime patungo sa compile time: ang code ay binuo bago ilunsad ang app, na nag-aalis ng reflection overhead at nagpapabuti sa performance.
// KSP: code generation sa halip na reflection
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP ay bumubuo ng ConfigSerializer nang walang reflection
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
Sa halimbawang ito, ang @Serializable ay anotasyon ng Kotlinx.serialization. Ang KSP (Kotlin Symbol Processing) ay nagsusuri ng source code sa yugto ng kompilasyon, nakakahanap ng lahat ng @Serializable na klase at bumubuo ng mga serializer. Sa panahon ng pagpapatakbo ng app, hindi ginagamit ang reflection — ang serializer ay naka-compile na sa machine code.
Ang code generation ay nagbibigay ng mas mahusay na performance, kaligtasan ng mga tipo at mas maliit na binary file (ang dead code elimination ay nag-aalis ng mga hindi ginagamit na reflection dependency). Nananatiling kailangan ang reflection para sa mga gawain kung saan hindi kilala ang mga tipo sa yugto ng kompilasyon: dynamic na pag-load ng mga plugin, runtime proxy, instrumentasyon ng mga test. Ayon sa Kotlin, ang Kotlinx.serialization na may KSP ay 3–5 beses na mas mabilis kaysa sa Gson na batay sa reflection.
Ang Reflection sa mga mobile platform ay may mga limitasyon sa seguridad at performance. Ang Android mula sa API 28 (Pie) ay nililimitahan ang setAccessible para sa mga non-SDK na interface — ang pagtatangkang buksan ang nakatagong sistema na pamamaraan ay nagdudulot ng exception o babala. Ang iOS na may Swift ay hindi sumusuporta sa reflection sa klasikal na kahulugan: ang Swift Mirror API ay nagbibigay lamang ng pagbasa ng mga property (name, value) nang walang pagbabago o pagtawag ng mga pamamaraan.
Ang Google Play ay tumatanggi sa mga app na gumagamit ng reflection upang lampasan ang mga limitasyon ng platform: pagpapalit ng mga sistema na serbisyo, pagbabago ng mga SELinux policy, pagbasa ng mga protektadong permiso. Bina-block din ng Apple ang mga app na tumatawag ng mga pribadong API sa pamamagitan ng reflection — ang pagsusuri ng App Review ay nagsi-scan ng binary file para sa mga string signature ng objc_msgSend na may kilalang pribadong selector.
ProGuard/R8 — isa pang limitasyon. Ang obfuscation at minification ng code ay nagpapalit ng pangalan ng mga klase at pamamaraan sa maikling pangalan (a, b, c). Kung ang code ay gumagamit ng Class.forName(„com.example.MyClass”), masisira ito pagkatapos ng obfuscation. Ang solusyon — mga keep na panuntunan sa proguard-rules.pro:
// ProGuard keep na mga panuntunan para sa reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
Ang mga panuntunang -keep ay nagsasabi sa R8 na huwag palitan ng pangalan ang mga klase na ginagamit sa pamamagitan ng reflection. Kung wala ang mga panuntunang ito, ang na-obfuscate na app ay mag-crash sa ClassNotFoundException — hindi mahahanap ng runtime ang klase batay sa string na pangalan na nabago na.
Mga madalas itanong
Oo, ang reflection ay 10–100 beses na mas mabagal kaysa sa direktang tawag. Ang mga pangunahing dahilan: kawalan ng JIT optimizations (inlining, devirtualization), pag-iimpake ng mga parameter at pagsusuri ng mga tipo sa bawat tawag. Para sa production code, inirerekomenda na palitan ang reflection ng code generation sa pamamagitan ng KSP o annotation processing.
Ang Java reflection ay gumagana sa pamamagitan ng Class, Method, Field at nangangailangan ng setAccessible para sa mga private na miyembro. Ang Kotlin reflection ay gumagamit ng KClass, KFunction, KProperty at sumusuporta sa sealed class, data class, mga coroutine (suspend function) at null-safety. Ang Kotlin reflection ay batay sa Java reflection, ngunit nagdaragdag ng type-safe na API.
Magdagdag ng ProGuard/R8 keep na mga panuntunan para sa mga klase, pamamaraan at field na ginagamit sa pamamagitan ng reflection. Para sa bawat Class.forName(), getDeclaredMethod(), getDeclaredField() ay dapat may kaukulang -keep directive. Ang mga tool tulad ng GreenDAO at Room ay awtomatikong bumubuo ng mga keep na panuntunan.
Ang Swift ay walang reflection sa buong kahulugan. Ang Mirror API (Swift 2+) ay nagpapahintulot sa pagbasa ng mga property ng structure o klase: pangalan, halaga, tipo. Ang pagtawag ng mga pamamaraan, pagbabago ng mga field at paglikha ng mga instance batay sa tipo ay hindi posible. Para dito ginagamit ang Objective-C Runtime kapag nagmamana mula sa NSObject na may @objc dynamic.
Gson (JSON serialization), Retrofit (paglikha ng mga implementation ng interface sa pamamagitan ng dynamic proxy), Mockito (paglikha ng mga mock), Koin (dependency injection), Room (pagsusuri ng Entity sa yugto ng kompilasyon sa pamamagitan ng KAPT), Firebase Crashlytics (pagsusuri ng stack trace). Karamihan sa mga library ay lumilipat sa code generation na may KSP/KAPT.
Mga konklusyon
Gagawa kami ng mobile application na turnkey
Gumagawa ang IT Sectr ng mga iOS at Android application para sa mga startup at negosyo mula noong 2017. Magpapayo kami sa iyo at magmumungkahi ng pinakamahusay na solusyon.
Basahin din