Reflection (reflexe) — mechanismus běhového prostředí, který umožňuje kódu zkoumat vlastní strukturu: získávat třídy, metody, pole a anotace bez znalosti typů ve fázi kompilace. Tento nástroj stojí v základu mnoha mobilních frameworků — serializace JSON (Gson, Moshi), dependency injection (Dagger, Koin) a testovacích runnerů (JUnit, XCTest). Podle Oracle Java Reflection Tutorial, 2024 je reflection povinným prvkem platformy Java, který používají všechny velké knihovny.
Hlavní body
Reflection — schopnost programu pozorovat a měnit vlastní strukturu a chování za běhu. V objektově orientovaných jazycích to znamená získávání objektů Class, Method, Field a Constructor, které představují prvky programu jako data dostupná pro čtení a volání.
Termín „reflection” byl zaveden v komunitě umělé inteligence v roce 1982 (Brian Cantwell Smith) a implementován v jazyce Smalltalk. V mobilním vývoji se reflection poprvé objevil v Java ME a Objective-C (1986, NextStep). Dnes má každá velká mobilní platforma své vlastní reflection API: Java/Kotlin pro Android, Objective-C Runtime pro iOS, Swift Mirror API pro Swift.
Mechanismus reflection je založen na metadatech, která kompilátor ukládá do bajtkódu nebo binárního souboru. Android ukládá úplné informace o třídách do souborů DEX, iOS — do segmentu __objc_classlist v Mach-O. Runtime tato metadata načte do paměti a poskytuje API pro jejich procházení.
Java Reflection API je postaveno kolem třídy java.lang.Class. Jakýkoli objekt v Javě lze převést na Class přes .getClass() nebo Class.forName(). Z Class se získávají všechny metody, pole, konstruktory, anotace a nadtřídy. Kotlin dědí Java reflection a přidává vlastní KClass, KFunction, KProperty z balíčku 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("Vlastnost: ${prop.name}, typ: ${prop.returnType}")
}
}
V tomto příkladu KClass poskytuje metadata data class User. declaredMemberProperties vrací seznam vlastností s jejich typy a gettery. Reflection v Kotlinu je úzce integrován s korutinami: KFunction podporuje modifikátor suspend, což umožňuje volat asynchronní metody přes reflection.
Java reflection pracuje s Class<?>, Method.setAccessible() a Field.get(). setAccessible(true) vypíná kontrolu přístupu Java language access control pro private prvky. Je to mocný, ale nebezpečný mechanismus: na Androidu od API 28 může volání setAccessible na skrytých systémových metodách způsobit InaccessibleObjectException.
// Java reflection: volání soukromé metody
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");
Kód demonstruje Class.forName() — dynamické načítání třídy podle řetězcového názvu. To je základ pluginových architektur: třída může být ve fázi kompilace neznámá, ale v runtime načtena a spuštěna přes reflection. getDeclaredMethod(„privateMethod”, ...) najde metodu podle názvu a typů parametrů a invoke ji spustí.
Objective-C runtime poskytuje funkce class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject. Na rozdíl od Javy Objective-C standardně neskrývá soukromé metody — runtime vidí všechny metody třídy. To vysvětluje, proč method swizzling funguje bez setAccessible: runtime nemá zapouzdření na úrovni metadat.
Reflection se používá v klíčových knihovnách mobilního vývoje. Serializace JSON (Gson, Moshi, Kotlinx.serialization) získává vlastnosti objektu přes reflection a přiřazuje je ke klíčům JSON. Dependency injection (Dagger, Koin, Swinject) analyzuje konstruktory a pole pro automatické vkládání závislostí. ORM knihovny (Room, Realm) používají reflection k mapování tříd na tabulky databáze.
Každé z těchto použití funguje právě v runtime — kód předem neví, s jakými třídami se setká. Reflection poskytuje univerzální mechanismus pro překonání této neznámé za cenu výkonu a bezpečnosti.
Reflection je 10–100krát pomalejší než přímé volání metod. Důvod — chybějící optimalizace JIT (devirtualization, inlining), kontrola typů při každém volání a balení parametrů do Object[]/varargs. ART na Androidu 14 nemůže inline-optimalizovat reflection volání, protože cílová metoda je neznámá až do okamžiku provedení.
| Operace | Přímé volání | Přes Reflection | Zpomalení |
|---|---|---|---|
| Volání metody bez parametrů | ~3 ns | ~120 ns | 40x |
| Čtení pole int | ~1 ns | ~85 ns | 85x |
| Volání metody se 2 parametry | ~4 ns | ~250 ns | 62x |
| Vytvoření instance přes konstruktor | ~5 ns | ~180 ns | 36x |
| Určení třídy podle řetězce | — | ~800 ns | — |
Data byla získána na Google Pixel 8 (Android 14, ART). Výkon reflection se s každou verzí Androidu zlepšuje: na Androidu 9 bylo volání přes Method.invoke() 150krát pomalejší než přímé, na Androidu 14 — 40krát. ART používá vestavěné mechanismy method handle pro optimalizaci.
Pro kritické úseky z hlediska výkonu vývojáři nahrazují reflection kódogenerací: Dagger používá annotation processing místo hledání v runtime, Kotlinx.serialization generuje serializátory přes KSP, Moshi přizpůsobuje @JsonClass(generateAdapter = true) pro codegen ve fázi kompilace.
Annotation processing (KAPT, KSP) a code generation — hlavní alternativy k reflection v mobilním vývoji. Přesouvají analýzu metadat z runtime do compile time: kód se generuje před spuštěním aplikace, což eliminuje režii reflection a zlepšuje výkon.
// KSP: code generation místo reflection
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP generuje ConfigSerializer bez reflection
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
V tomto příkladu je @Serializable anotace Kotlinx.serialization. KSP (Kotlin Symbol Processing) analyzuje zdrojový kód ve fázi kompilace, najde všechny třídy @Serializable a generuje serializátory. Během provádění aplikace se reflection nepoužívá — serializátor je již zkompilován do strojového kódu.
Code generation poskytuje lepší výkon, typovou bezpečnost a menší velikost binárního souboru (dead code elimination odstraňuje nepoužívané reflection závislosti). Reflection zůstává nezbytný pro úlohy, kde jsou typy neznámé ve fázi kompilace: dynamické načítání pluginů, runtime-proxy, instrumentace testů. Podle Kotlinu je Kotlinx.serialization s KSP 3–5krát rychlejší než Gson založený na reflection.
Reflection na mobilních platformách má bezpečnostní a výkonnostní omezení. Android od API 28 (Pie) omezuje setAccessible pro non-SDK rozhraní — pokus o otevření skryté systémové metody vede k výjimce nebo varování. iOS se Swiftem nepodporuje reflection v klasickém smyslu: Swift Mirror API poskytuje pouze čtení vlastností (name, value) bez úprav nebo volání metod.
Google Play odmítá aplikace používající reflection k obcházení omezení platformy: náhrada systémových služeb, modifikace politik SELinux, čtení chráněných oprávnění. Apple také blokuje aplikace volající privátní API přes reflection — kontrola App Review skenuje binární soubor na řetězcové signatury objc_msgSend se známými privátními selektory.
ProGuard/R8 — další omezení. Obfuskace a minifikace kódu přejmenovává třídy a metody na krátké názvy (a, b, c). Pokud kód používá Class.forName(„com.example.MyClass”), po obfuskaci se rozbije. Řešení — keep pravidla v proguard-rules.pro:
// ProGuard keep pravidla pro reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
Pravidla -keep informují R8, aby nepřejmenovával třídy používané přes reflection. Bez těchto pravidel obfuskovaná aplikace spadne s ClassNotFoundException — runtime nenajde třídu podle řetězcového názvu, který se změnil.
Často kladené otázky
Ano, reflection je 10–100krát pomalejší než přímé volání. Hlavní příčiny: chybějící optimalizace JIT (inlining, devirtualization), balení parametrů a kontrola typů při každém volání. Pro produkční kód se doporučuje nahradit reflection code generation přes KSP nebo annotation processing.
Java reflection funguje přes Class, Method, Field a vyžaduje setAccessible pro private členy. Kotlin reflection používá KClass, KFunction, KProperty a podporuje sealed class, data class, korutiny (suspend funkce) a null-safety. Kotlin reflection je založen na Java reflection, ale přidává type-safe API.
Přidat ProGuard/R8 keep pravidla pro třídy, metody a pole používané přes reflection. Pro každé Class.forName(), getDeclaredMethod(), getDeclaredField() musí existovat odpovídající direktiva -keep. Nástroje jako GreenDAO a Room automaticky generují keep pravidla.
Swift nemá reflection v plném smyslu. Mirror API (Swift 2+) umožňuje číst vlastnosti struktury nebo třídy: název, hodnotu, typ. Volání metod, úprava polí a vytváření instancí podle typu nejsou možné. K tomu se používá Objective-C Runtime při dědění z NSObject s @objc dynamic.
Gson (serializace JSON), Retrofit (vytváření implementací rozhraní přes dynamic proxy), Mockito (vytváření mocků), Koin (dependency injection), Room (kontrola Entity ve fázi kompilace přes KAPT), Firebase Crashlytics (analýza stack trace). Většina knihoven přechází na code generation s KSP/KAPT.
Závěry
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také