Reflection (reflektion) — en runtime-mekanism som gör att koden kan utforska sin egen struktur: hämta klasser, metoder, fält och annoteringar utan att känna till typerna i kompileringssteget. Detta verktyg ligger till grund för många mobila ramverk — JSON-serialisering (Gson, Moshi), dependency injection (Dagger, Koin) och testlöpare (JUnit, XCTest). Enligt Oracle Java Reflection Tutorial, 2024 är reflection ett obligatoriskt inslag i Java-plattformen som används av alla stora bibliotek.
Viktigast
Reflection — programmets förmåga att observera och modifiera sin egen struktur och sitt beteende under körning. I objektorienterade språk innebär detta att hämta objekten Class, Method, Field och Constructor, som representerar programmets element som data tillgänglig för läsning och anrop.
Termen „reflection” introducerades i AI-gemenskapen 1982 (Brian Cantwell Smith) och implementerades i språket Smalltalk. I mobil utveckling dök reflection först upp i Java ME och Objective-C (1986, NextStep). Idag har varje stor mobil plattform sitt eget reflection-API: Java/Kotlin för Android, Objective-C Runtime för iOS, Swift Mirror API för Swift.
Reflection-mekanismen bygger på metadata som kompilatorn lagrar i bytecode eller i en binär fil. Android lagrar fullständig information om klasser i DEX-filer, iOS — i segmentet __objc_classlist i Mach-O. Runtime läser in dessa metadata i minnet och tillhandahåller ett API för att gå igenom dem.
Java Reflection API byggs runt klassen java.lang.Class. Vilket objekt som helst i Java kan omvandlas till Class via .getClass() eller Class.forName(). Från Class extraheras alla metoder, fält, konstruktorer, annoteringar och superklasser. Kotlin ärver Java-reflection och lägger till egna KClass, KFunction, KProperty från paketet 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("Egenskap: ${prop.name}, typ: ${prop.returnType}")
}
}
I detta exempel tillhandahåller KClass metadata för data class User. declaredMemberProperties returnerar en lista över egenskaper med deras typer och getters. Kotlin-reflection är nära integrerat med coroutines: KFunction stödjer suspend-modifieraren, vilket gör att asynkrona metoder kan anropas via reflection.
Java-reflection arbetar med Class<?>, Method.setAccessible() och Field.get(). setAccessible(true) stänger av åtkomstkontrollen Java language access control för private-element. Detta är en kraftfull men farlig mekanism: på Android kan setAccessible-anrop på dolda systemmetoder från API 28 orsaka InaccessibleObjectException.
// Java-reflection: anrop av privat metod
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");
Koden demonstrerar Class.forName() — dynamisk inläsning av en klass baserat på strängnamn. Detta är grunden för plugin-arkitekturer: en klass kan vara okänd i kompileringssteget men laddas och köras via reflection i runtime. getDeclaredMethod(„privateMethod”, ...) hittar metoden efter namn och parametertyper, och invoke kör den.
Objective-C runtime tillhandahåller funktionerna class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject. Till skillnad från Java döljer Objective-C inte privata metoder som standard — runtime ser alla metoder i klassen. Detta förklarar varför method swizzling fungerar utan setAccessible: runtime har ingen inkapsling på metadatanivå.
Reflection används i nyckelbiblioteken för mobil utveckling. JSON-serialisering (Gson, Moshi, Kotlinx.serialization) hämtar objektets egenskaper via reflection och matchar dem mot JSON-nycklar. Dependency injection (Dagger, Koin, Swinject) analyserar konstruktorer och fält för automatisk injicering av beroenden. ORM-bibliotek (Room, Realm) använder reflection för att mappa klasser till databasens tabeller.
Var och en av dessa tillämpningar fungerar just i runtime — koden vet inte i förväg vilka klasser den kommer att möta. Reflection tillhandahåller en universell mekanism för att hantera denna okunskap till priset av prestanda och säkerhet.
Reflection är 10–100 gånger långsammare än att anropa metoder direkt. Orsaken — avsaknaden av JIT-optimeringar (devirtualization, inlining), typkontroll vid varje anrop och paketering av parametrar i Object[]/varargs. ART på Android 14 kan inte inline-optimera reflection-anrop, eftersom målmetoden är okänd fram till exekveringstillfället.
| Operation | Direkt anrop | Via Reflection | Inbromsning |
|---|---|---|---|
| Anropa metod utan parametrar | ~3 ns | ~120 ns | 40x |
| Läsa int-fält | ~1 ns | ~85 ns | 85x |
| Anropa metod med 2 parametrar | ~4 ns | ~250 ns | 62x |
| Skapa instans via konstruktor | ~5 ns | ~180 ns | 36x |
| Fastställa klass efter sträng | — | ~800 ns | — |
Data har hämtats på en Google Pixel 8 (Android 14, ART). Reflection-prestandan förbättras med varje version av Android: på Android 9 var ett anrop via Method.invoke() 150 gånger långsammare än direkt, på Android 14 — 40 gånger. ART använder inbyggda method handle-mekanismer för optimering.
För prestandakritiska delar ersätter utvecklare reflection med kodgenerering: Dagger använder annotation processing i stället för runtime-sökning, Kotlinx.serialization genererar serialiserare via KSP, Moshi anpassar @JsonClass(generateAdapter = true) för codegen i kompileringssteget.
Annotation processing (KAPT, KSP) och code generation — de främsta alternativen till reflection i mobil utveckling. De flyttar metadataanalysen från runtime till compile time: koden genereras före appens start, vilket eliminerar reflection-overhead och förbättrar prestandan.
// KSP: code generation i stället för reflection
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP genererar ConfigSerializer utan reflection
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
I detta exempel är @Serializable en annotering från Kotlinx.serialization. KSP (Kotlin Symbol Processing) analyserar källkoden i kompileringssteget, hittar alla @Serializable-klasser och genererar serialiserare. Under appens körning används inte reflection — serialiseraren är redan kompilerad till maskinkod.
Code generation ger bättre prestanda, typsäkerhet och en mindre binär fil (dead code elimination tar bort oanvända reflection-beroenden). Reflection förblir nödvändig för uppgifter där typerna är okända i kompileringssteget: dynamisk inläsning av plugins, runtime-proxy, instrumentering av tester. Enligt Kotlin är Kotlinx.serialization med KSP 3–5 gånger snabbare än Gson, som bygger på reflection.
Reflection har säkerhets- och prestandabegränsningar på mobila plattformar. Android begränsar från API 28 (Pie) setAccessible för non-SDK-gränssnitt — ett försök att öppna en dold systemmetod leder till ett undantag eller en varning. iOS med Swift stödjer inte reflection i klassisk mening: Swift Mirror API tillhandahåller endast läsning av egenskaper (name, value) utan modifiering eller anrop av metoder.
Google Play avvisar appar som använder reflection för att kringgå plattformens begränsningar: ersättning av systemtjänster, ändring av SELinux-policyer, läsning av skyddade behörigheter. Apple blockerar också appar som anropar privata API:er via reflection — App Review-kontrollen skannar den binära filen efter strängsignaturer för objc_msgSend med kända privata selectorer.
ProGuard/R8 — ytterligare en begränsning. Obfuskering och minifiering av kod byter namn på klasser och metoder till korta namn (a, b, c). Om koden använder Class.forName(„com.example.MyClass”) går den sönder efter obfuskering. Lösningen — keep-regler i proguard-rules.pro:
// ProGuard keep-regler för reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
Reglerna -keep uppmanar R8 att inte byta namn på klasser som används via reflection. Utan dessa regler kraschar den obfuskerade appen med ClassNotFoundException — runtime kan inte hitta klassen efter det ändrade strängnamnet.
Vanliga frågor
Ja, reflection är 10–100 gånger långsammare än ett direkt anrop. De främsta orsakerna: avsaknaden av JIT-optimeringar (inlining, devirtualization), paketering av parametrar och typkontroll vid varje anrop. För produktionskod rekommenderas att ersätta reflection med code generation via KSP eller annotation processing.
Java-reflection fungerar via Class, Method, Field och kräver setAccessible för private-medlemmar. Kotlin-reflection använder KClass, KFunction, KProperty och stödjer sealed class, data class, coroutines (suspend-funktioner) och null-safety. Kotlin-reflection bygger på Java-reflection men lägger till ett type-safe API.
Lägg till ProGuard/R8 keep-regler för klasser, metoder och fält som används via reflection. För varje Class.forName(), getDeclaredMethod(), getDeclaredField() måste det finnas en motsvarande -keep-direktiv. Verktyg som GreenDAO och Room genererar automatiskt keep-regler.
Swift har inte reflection i full bemärkelse. Mirror API (Swift 2+) gör det möjligt att läsa egenskaper hos en struct eller klass: namn, värde, typ. Anrop av metoder, ändring av fält och skapande av instanser efter typ är inte möjligt. För detta används Objective-C Runtime vid arv från NSObject med @objc dynamic.
Gson (JSON-serialisering), Retrofit (skapande av gränssnittsimplementationer via dynamic proxy), Mockito (skapande av mockar), Koin (dependency injection), Room (kontroll av Entity i kompileringssteget via KAPT), Firebase Crashlytics (stack trace-analys). De flesta bibliotek går över till code generation med KSP/KAPT.
Slutsatser
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också