Reflection (reflexão) é um mecanismo de runtime que permite ao código inspecionar sua própria estrutura: obter classes, métodos, campos e anotações sem conhecer os tipos em tempo de compilação. Essa ferramenta está na base de muitos frameworks móveis — serialização JSON (Gson, Moshi), injeção de dependências (Dagger, Koin) e runners de testes (JUnit, XCTest). De acordo com o Tutorial de Reflection da Oracle Java, 2024, o reflection é um elemento obrigatório da plataforma Java, usado por todas as grandes bibliotecas.
Pontos principais
Reflection é a capacidade de um programa observar e modificar sua própria estrutura e comportamento durante a execução. Em linguagens orientadas a objetos, isso significa obter objetos Class, Method, Field e Constructor, que representam os elementos do programa como dados disponíveis para leitura e invocação.
O termo “reflection” foi introduzido na comunidade de inteligência artificial em 1982 (Brian Cantwell Smith) e implementado na linguagem Smalltalk. No desenvolvimento móvel, o reflection apareceu pela primeira vez em Java ME e Objective-C (1986, NextStep). Hoje cada grande plataforma móvel tem sua própria API de reflection: Java/Kotlin para Android, Objective-C Runtime para iOS, Swift Mirror API para Swift.
O mecanismo de reflection é baseado nos metadados que o compilador salva no bytecode ou binário. O Android armazena informações completas sobre as classes em arquivos DEX; o iOS as armazena na seção __objc_classlist do segmento Mach-O. O runtime carrega esses metadados na memória e fornece uma API para percorrê-los.
Java Reflection API é construída em torno da classe java.lang.Class. Qualquer objeto em Java pode ser convertido em Class por meio de .getClass() ou Class.forName(). A partir de Class são extraídos todos os métodos, campos, construtores, anotações e superclasses. O Kotlin herda a reflection do Java e adiciona suas próprias KClass, KFunction, KProperty do pacote 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("Propriedade: ${prop.name}, tipo: ${prop.returnType}")
}
}
Neste exemplo, KClass fornece os metadados da data class User. declaredMemberProperties retorna uma lista de propriedades com seus tipos e getters. A reflection do Kotlin é estreitamente integrada às coroutines: KFunction suporta o modificador suspend, o que permite chamar métodos assíncronos por meio de reflection.
A reflection do Java trabalha com Class<?>, Method.setAccessible() e Field.get(). setAccessible(true) desativa as verificações de controle de acesso da linguagem Java para elementos privados. Esse é um mecanismo poderoso, mas perigoso: no Android a partir da API 28, chamar setAccessible em métodos ocultos do sistema pode causar InaccessibleObjectException.
// Java reflection: invocação de um método privado
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");
O código demonstra Class.forName() — o carregamento dinâmico de uma classe por nome de string. Essa é a base das arquiteturas de plugins: uma classe pode ser desconhecida em tempo de compilação, mas carregada e executada por meio de reflection em runtime. getDeclaredMethod(“privateMethod”, ...) encontra um método por nome e tipos de parâmetros, e invoke o executa.
O runtime do Objective-C fornece as funções class_copyMethodList, class_copyPropertyList, objc_getAssociatedObject. Diferentemente do Java, o Objective-C não oculta métodos privados por padrão — o runtime vê todos os métodos da classe. Isso explica por que o method swizzling funciona sem setAccessible: o runtime não tem encapsulamento no nível de metadados.
Reflection é usado em bibliotecas-chave do desenvolvimento móvel. A serialização JSON (Gson, Moshi, Kotlinx.serialization) obtém as propriedades do objeto por meio de reflection e as compara com as chaves JSON. A injeção de dependências (Dagger, Koin, Swinject) analisa construtores e campos para injeção automática de dependências. As bibliotecas ORM (Room, Realm) usam reflection para mapear classes em tabelas de banco de dados.
Cada uma dessas aplicações funciona exatamente em runtime — o código não sabe de antemão com quais classes vai se deparar. O Reflection fornece um mecanismo universal para superar essa incerteza ao custo de desempenho e segurança.
Reflection é de 10 a 100 vezes mais lento que chamadas diretas a métodos. O motivo é a falta de otimizações JIT (devirtualization, inlining), a verificação de tipos em cada chamada e o empacotamento de parâmetros em Object[]/varargs. O ART no Android 14 não consegue otimizar com inline chamadas de reflection porque o método alvo é desconhecido até o momento da execução.
| Operação | Chamada direta | Através de Reflection | Ralentização |
|---|---|---|---|
| Chamada de método sem parâmetros | ~3 ns | ~120 ns | 40x |
| Leitura de um campo int | ~1 ns | ~85 ns | 85x |
| Chamada de método com 2 parâmetros | ~4 ns | ~250 ns | 62x |
| Criação de instância pelo construtor | ~5 ns | ~180 ns | 36x |
| Resolução de classe por string | — | ~800 ns | — |
Os dados foram obtidos em um Google Pixel 8 (Android 14, ART). O desempenho do reflection melhora a cada versão do Android: no Android 9, uma chamada por meio de Method.invoke() era 150 vezes mais lenta que a direta; no Android 14, é 40 vezes. O ART usa mecanismos internos de method handle para otimização.
Para trechos críticos de desempenho, os desenvolvedores substituem o reflection por geração de código: o Dagger usa processamento de anotações em vez de busca em runtime, o Kotlinx.serialization gera serializadores por meio de KSP, o Moshi adapta @JsonClass(generateAdapter = true) para codegen em tempo de compilação.
Processamento de anotações (KAPT, KSP) e geração de código são as principais alternativas ao reflection no desenvolvimento móvel. Eles movem a análise de metadados do runtime para o tempo de compilação: o código é gerado antes de a aplicação iniciar, o que elimina o overhead do reflection e melhora o desempenho.
// KSP: geração de código em vez de reflection
@Serializable
data class Config(
val apiUrl: String,
val timeout: Int
)
// KSP gera ConfigSerializer sem reflection
fun loadConfig(json: String): Config {
return Config.serializer().decodeFromString(json)
}
Neste exemplo, @Serializable é a anotação do Kotlinx.serialization. O KSP (Kotlin Symbol Processing) analisa o código-fonte em tempo de compilação, encontra todas as classes @Serializable e gera os serializadores. Durante a execução da aplicação, o reflection não é usado — o serializador já está compilado em código de máquina.
A geração de código oferece melhor desempenho, segurança de tipos e tamanho de binário menor (o dead code elimination remove dependências de reflection não utilizadas). O reflection permanece necessário para tarefas em que os tipos são desconhecidos em tempo de compilação: carregamento dinâmico de plugins, proxies em runtime, instrumentação de testes. Segundo o Kotlin, o Kotlinx.serialization com KSP é de 3 a 5 vezes mais rápido que o Gson, baseado em reflection.
Reflection nas plataformas móveis tem limitações de segurança e desempenho. O Android a partir da API 28 (Pie) restringe setAccessible para interfaces non-SDK — tentar abrir um método oculto do sistema causa uma exceção ou aviso. O iOS com Swift não suporta reflection no sentido clássico: a Swift Mirror API fornece apenas a leitura de propriedades (name, value), sem modificação ou chamadas a métodos.
Google Play rejeita aplicativos que usam reflection para contornar as restrições da plataforma: substituição de serviços do sistema, modificação de políticas SELinux, leitura de permissões protegidas. A Apple também bloqueia aplicativos que chamam APIs privadas por meio de reflection — a revisão do App Review escaneia o binário em busca de assinaturas de string de objc_msgSend com seletores privados conhecidos.
ProGuard/R8 é outra limitação. A ofuscação e a minificação de código renomeiam classes e métodos para nomes curtos (a, b, c). Se o código usa Class.forName(“com.example.MyClass”), ele quebrará após a ofuscação. A solução são as keep rules no proguard-rules.pro:
// Regras keep do ProGuard para reflection
-keep class com.example.** { *; }
-keep class * implements java.io.Serializable { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName ;
}
-keepattributes Signature, InnerClasses, EnclosingMethod
As regras -keep informam ao R8 para não renomear classes usadas por meio de reflection. Sem essas regras, um aplicativo ofuscado falhará com ClassNotFoundException — o runtime não conseguirá encontrar a classe pelo nome de string que mudou.
Perguntas frequentes
Sim, o reflection é de 10 a 100 vezes mais lento que uma chamada direta. As principais razões: falta de otimizações JIT (inlining, devirtualization), empacotamento de parâmetros e verificação de tipos em cada chamada. Para código de produção, recomenda-se substituir o reflection por geração de código via KSP ou processamento de anotações.
A reflection do Java funciona por meio de Class, Method, Field e exige setAccessible para membros privados. A reflection do Kotlin usa KClass, KFunction, KProperty e suporta sealed class, data class, coroutines (funções suspend) e null-safety. A reflection do Kotlin é baseada na do Java, mas adiciona uma API type-safe.
Adicione keep rules do ProGuard/R8 para classes, métodos e campos usados por meio de reflection. Para cada Class.forName(), getDeclaredMethod(), getDeclaredField() deve haver uma diretiva -keep correspondente. Ferramentas como GreenDAO e Room geram keep rules automaticamente.
O Swift não tem reflection no sentido pleno. Mirror API (Swift 2+) permite ler as propriedades de uma struct ou classe: nome, valor, tipo. Chamar métodos, modificar campos e criar instâncias por tipo são impossíveis. Para isso, usa-se o Objective-C Runtime ao herdar de NSObject com @objc dynamic.
Gson (serialização JSON), Retrofit (criação de implementações de interfaces por meio de proxy dinâmico), Mockito (criação de mocks), Koin (injeção de dependências), Room (verificação de Entity em tempo de compilação via KAPT), Firebase Crashlytics (análise de stack trace). A maioria das bibliotecas migra para geração de código com KSP/KAPT.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também