Reflection dans le développement d'applications — ce que c'est, les mécanismes de réflexion et comment les appliquer

Auteur : IT Sectr Publié le : 2026-05-17 Temps de lecture : 8 min

Reflection (réflexion) est un mécanisme d'exécution qui permet au code d'inspecter sa propre structure : obtenir des classes, des méthodes, des champs et des annotations sans connaître les types au moment de la compilation. Cet outil est à la base de nombreux frameworks mobiles — la sérialisation JSON (Gson, Moshi), l'injection de dépendances (Dagger, Koin) et les runners de tests (JUnit, XCTest). Selon le Tutoriel de Reflection d'Oracle Java, 2024, reflection est un élément obligatoire de la plateforme Java, utilisé par toutes les grandes bibliothèques.

Points clés

  • Reflection est l'accès aux métadonnées des classes, méthodes et champs pendant l'exécution du programme.
  • Java Reflection API fournit les classes Class, Method, Field et Constructor pour l'analyse dynamique.
  • La réflexion de Kotlin utilise KClass et KFunction, intégrées avec les coroutines et la sérialisation.
  • Objective-C Runtime est une forme de reflection via class_copyMethodList et objc_getClass.
  • Les performances de reflection sont 10 à 100 fois inférieures aux appels directs en raison de l'absence d'optimisations JIT.

Qu'est-ce que Reflection ?

Reflection est la capacité d'un programme à observer et à modifier sa propre structure et son comportement pendant l'exécution. Dans les langages orientés objet, cela signifie obtenir des objets Class, Method, Field et Constructor, qui représentent les éléments du programme comme des données disponibles pour la lecture et l'appel.

Le terme « reflection » a été introduit dans la communauté de l'intelligence artificielle en 1982 (Brian Cantwell Smith) et implémenté dans le langage Smalltalk. Dans le développement mobile, reflection est apparu pour la première fois dans Java ME et Objective-C (1986, NextStep). Aujourd'hui, chaque grande plateforme mobile possède sa propre API de reflection : Java/Kotlin pour Android, Objective-C Runtime pour iOS, Swift Mirror API pour Swift.

Le mécanisme de reflection repose sur les métadonnées que le compilateur enregistre dans le bytecode ou le binaire. Android stocke des informations complètes sur les classes dans les fichiers DEX, iOS dans la section __objc_classlist du segment Mach-O. Le runtime charge ces métadonnées en mémoire et fournit une API pour les parcourir.

Comment Reflection fonctionne en Java et Kotlin

Java Reflection API est construite autour de la classe java.lang.Class. N'importe quel objet en Java peut être converti en Class via .getClass() ou Class.forName(). À partir de Class, on extrait toutes les méthodes, champs, constructeurs, annotations et superclasses. Kotlin hérite de la reflection de Java et ajoute ses propres KClass, KFunction, KProperty du package 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("Propriété : ${prop.name}, type : ${prop.returnType}")
    }
}

Dans cet exemple, KClass fournit les métadonnées de la data class User. declaredMemberProperties renvoie une liste de propriétés avec leurs types et getters. La reflection de Kotlin est étroitement intégrée aux coroutines : KFunction prend en charge le modificateur suspend, ce qui permet d'appeler des méthodes asynchrones via reflection.

Java Reflection : Class, Method, Field

La reflection de Java travaille avec Class<?>, Method.setAccessible() et Field.get(). setAccessible(true) désactive les contrôles de contrôle d'accès du langage Java pour les éléments privés. C'est un mécanisme puissant mais dangereux : sur Android à partir de l'API 28, appeler setAccessible sur des méthodes système cachées peut provoquer InaccessibleObjectException.

java
// Java reflection : appel d'une méthode privée
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");

Le code démontre Class.forName() — le chargement dynamique d'une classe par nom de chaîne. C'est la base des architectures de plugins : une classe peut être inconnue au moment de la compilation, mais chargée et exécutée via reflection au moment de l'exécution. getDeclaredMethod(“privateMethod”, ...) trouve une méthode par nom et types de paramètres, et invoke l'exécute.

Objective-C Runtime : un modèle alternatif de réflexion

Le runtime d'Objective-C fournit les fonctions class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject. Contrairement à Java, Objective-C ne cache pas les méthodes privées par défaut — le runtime voit toutes les méthodes de la classe. Cela explique pourquoi le method swizzling fonctionne sans setAccessible : le runtime n'a pas d'encapsulation au niveau des métadonnées.

Application de Reflection dans le développement mobile

Reflection est utilisé dans les bibliothèques clés du développement mobile. La sérialisation JSON (Gson, Moshi, Kotlinx.serialization) obtient les propriétés de l'objet via reflection et les compare aux clés JSON. L'injection de dépendances (Dagger, Koin, Swinject) analyse les constructeurs et les champs pour l'injection automatique de dépendances. Les bibliothèques ORM (Room, Realm) utilisent reflection pour mapper les classes sur les tables de base de données.

  • Sérialisation — Gson lit les champs déclarés d'un objet via Field.get() et crée du JSON selon les annotations @SerializedName.
  • Injection de dépendances — Dagger génère du code via le traitement d'annotations, Koin utilise la reflection de Kotlin pour la résolution au moment de l'exécution.
  • Tests — JUnit trouve les méthodes avec @Test via reflection et les appelle ; Mockito crée des mocks via un proxy dynamique.
  • Base de données — Room vérifie les champs Entity via Class.getDeclaredFields() au moment de la compilation (via KAPT/KSP).
  • Analytique et surveillance — Firebase Crashlytics obtient la stack trace via Throwable.getStackTrace(), basé sur reflection.

Chacune de ces applications fonctionne précisément au moment de l'exécution — le code ne sait pas à l'avance avec quelles classes il va rencontrer. Reflection fournit un mécanisme universel pour surmonter cette incertitude au prix des performances et de la sécurité.

Performances de Reflection : le coût de l'accès dynamique

Reflection est 10 à 100 fois plus lent que les appels directs de méthodes. La raison est l'absence d'optimisations JIT (devirtualization, inlining), la vérification des types à chaque appel et le regroupement des paramètres dans Object[]/varargs. ART sur Android 14 ne peut pas optimiser en inline les appels de reflection car la méthode cible est inconnue jusqu'au moment de l'exécution.

OpérationAppel directVia ReflectionRalentissement
Appel d'une méthode sans paramètres~3 ns~120 ns40x
Lecture d'un champ int~1 ns~85 ns85x
Appel d'une méthode avec 2 paramètres~4 ns~250 ns62x
Création d'une instance via le constructeur~5 ns~180 ns36x
Résolution d'une classe par chaîne~800 ns

Les données ont été obtenues sur un Google Pixel 8 (Android 14, ART). Les performances de reflection s'améliorent à chaque version d'Android : sur Android 9, un appel via Method.invoke() était 150 fois plus lent qu'un appel direct, sur Android 14, il est 40 fois. ART utilise des mécanismes intégrés de method handle pour l'optimisation.

Pour les sections critiques en performances, les développeurs remplacent reflection par la génération de code : Dagger utilise le traitement d'annotations au lieu de la recherche au moment de l'exécution, Kotlinx.serialization génère les sérialiseurs via KSP, Moshi adapte @JsonClass(generateAdapter = true) pour le codegen au moment de la compilation.

Alternatives à Reflection : annotations et génération de code

Le traitement d'annotations (KAPT, KSP) et la génération de code sont les principales alternatives à reflection dans le développement mobile. Ils déplacent l'analyse des métadonnées du moment de l'exécution au moment de la compilation : le code est généré avant le démarrage de l'application, ce qui élimine la surcharge de reflection et améliore les performances.

kotlin
// KSP : génération de code au lieu de reflection
@Serializable
data class Config(
    val apiUrl: String,
    val timeout: Int
)

// KSP génère ConfigSerializer sans reflection
fun loadConfig(json: String): Config {
    return Config.serializer().decodeFromString(json)
}

Dans cet exemple, @Serializable est l'annotation de Kotlinx.serialization. KSP (Kotlin Symbol Processing) analyse le code source au moment de la compilation, trouve toutes les classes @Serializable et génère les sérialiseurs. Pendant l'exécution de l'application, reflection n'est pas utilisé — le sérialiseur est déjà compilé en code machine.

Comparaison des approches

La génération de code offre de meilleures performances, la sécurité des types et une taille de binaire réduite (la suppression du code mort élimine les dépendances de reflection inutilisées). Reflection reste nécessaire pour les tâches où les types sont inconnus au moment de la compilation : chargement dynamique de plugins, proxies au moment de l'exécution, instrumentation des tests. Selon Kotlin, Kotlinx.serialization avec KSP est 3 à 5 fois plus rapide que Gson, basé sur reflection.

Limitations de Reflection sur Android et iOS

Reflection sur les plateformes mobiles a des limitations de sécurité et de performances. Android à partir de l'API 28 (Pie) restreint setAccessible pour les interfaces non-SDK — tenter d'ouvrir une méthode système cachée provoque une exception ou un avertissement. iOS avec Swift ne prend pas en charge reflection au sens classique : la Swift Mirror API ne fournit que la lecture des propriétés (name, value), sans modification ni appels de méthodes.

Google Play rejette les applications qui utilisent reflection pour contourner les restrictions de la plateforme : remplacement des services système, modification des politiques SELinux, lecture des autorisations protégées. Apple bloque également les applications qui appellent des API privées via reflection — le contrôle d'App Review scanne le binaire à la recherche de signatures de chaîne de objc_msgSend avec des sélecteurs privés connus.

ProGuard/R8 est une autre limitation. L'obfuscation et la minification du code renomment les classes et les méthodes en noms courts (a, b, c). Si le code utilise Class.forName(“com.example.MyClass”), il se brisera après l'obfuscation. La solution est les keep rules dans proguard-rules.pro :

groovy
// Règles keep de ProGuard pour reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
    @com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod

Les règles -keep indiquent à R8 de ne pas renommer les classes utilisées via reflection. Sans ces règles, une application obfusquée plantera avec ClassNotFoundException — le runtime ne pourra pas trouver la classe par le nom de chaîne qui a changé.

Questions fréquemment posées

Reflection est-il nuisible aux performances de l'application ?

Oui, reflection est 10 à 100 fois plus lent qu'un appel direct. Les principales raisons : l'absence d'optimisations JIT (inlining, devirtualization), le regroupement des paramètres et la vérification des types à chaque appel. Pour le code de production, il est recommandé de remplacer reflection par la génération de code via KSP ou le traitement d'annotations.

En quoi la reflection de Java diffère-t-elle de celle de Kotlin ?

La reflection de Java fonctionne via Class, Method, Field et exige setAccessible pour les membres privés. La reflection de Kotlin utilise KClass, KFunction, KProperty et prend en charge sealed class, data class, les coroutines (fonctions suspend) et la null-safety. La reflection de Kotlin est basée sur celle de Java, mais ajoute une API type-safe.

Comment éviter les problèmes d'obfuscation lors de l'utilisation de Reflection ?

Ajouter des keep rules ProGuard/R8 pour les classes, méthodes et champs utilisés via reflection. Pour chaque Class.forName(), getDeclaredMethod(), getDeclaredField(), il doit y avoir une directive -keep correspondante. Des outils comme GreenDAO et Room génèrent automatiquement des keep rules.

Existe-t-il Reflection en Swift ?

Swift n'a pas de reflection au sens plein. Mirror API (Swift 2+) permet de lire les propriétés d'une structure ou d'une classe : nom, valeur, type. Appeler des méthodes, modifier des champs et créer des instances par type sont impossibles. Pour cela, on utilise l'Objective-C Runtime lors de l'héritage de NSObject avec @objc dynamic.

Quelles bibliothèques utilisent Reflection sur Android ?

Gson (sérialisation JSON), Retrofit (création d'implémentations d'interfaces via proxy dynamique), Mockito (création de mocks), Koin (injection de dépendances), Room (vérification d'Entity au moment de la compilation via KAPT), Firebase Crashlytics (analyse de stack trace). La plupart des bibliothèques passent à la génération de code avec KSP/KAPT.

Résumé

  • Reflection est un mécanisme d'exécution pour accéder aux métadonnées des classes, méthodes et champs.
  • La reflection de Java utilise Class, Method, Field ; Kotlin utilise KClass, KFunction, KProperty avec l'intégration des coroutines.
  • Objective-C Runtime fournit class_copyMethodList et objc_getClass sans restrictions d'accès.
  • Reflection est plus lent qu'un appel direct de 10 à 100 fois en raison de l'absence d'optimisations JIT.
  • Alternatives — la génération de code (KSP, KAPT) et le traitement d'annotations — éliminent la surcharge de reflection.
  • ProGuard/R8 exige des keep rules pour les classes utilisées via Class.forName() et getDeclaredMethod().
  • Reflection est indispensable pour le chargement dynamique de plugins, la DI et les frameworks de test où les types sont inconnus au moment de la compilation.

Nous développerons une application mobile clé en main

IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.

Discuter du projet

Lisez aussi