Reflection em Java - Explorando Tipos com a Classe Class
A Reflection (Reflexão) é um mecanismo da plataforma Java que permite inspecionar e manipular classes, interfaces, campos e métodos em tempo de execução. Isso significa que um programa pode analisar suas próprias estruturas dinamicamente, sem conhecer os detalhes dos tipos em tempo de compilação.
A Reflection é particularmente útil na criação de frameworks, ferramentas de análise de código, bibliotecas de serialização e sistemas de plugins, onde é necessário interagir dinamicamente com tipos que não são conhecidos previamente ou cujo código fonte está indisponível.
A Classe Class
O ponto de entrada para a API de Reflection é a classe java.lang.Class
. Cada tipo em Java — seja uma classe, interface, enum ou tipo primitivo — possui um objeto Class
correspondente que armazena seus metadados. A seguir, alguns de seus métodos mais importantes:
Para obter membros da classe:
Field[] getFields()
: Retorna um array com os campos (Field
) públicos da classe e de suas superclasses.Field[] getDeclaredFields()
: Retorna um array com todos os campos declarados diretamente na classe (públicos, privados, etc.), mas não inclui campos herdados.Method[] getMethods()
: Retorna um array com os métodos (Method
) públicos da classe e de suas superclasses.Method[] getDeclaredMethods()
: Retorna um array com todos os métodos declarados diretamente na classe, mas não inclui métodos herdados.Constructor[] getConstructors()
: Retorna um array com os construtores (Constructor
) públicos da classe.Constructor[] getDeclaredConstructors()
: Retorna um array com todos os construtores declarados diretamente na classe.RecordComponent[] getRecordComponents()
: Se a classe for umrecord
, retorna um array que descreve seus componentes.
Para verificar características do tipo:
isAnonymousClass()
: Retornatrue
se for uma classe anônima.isArray()
: Retornatrue
se o tipo for um array.isPrimitive()
: Retornatrue
se for um tipo primitivo (comoint
,char
).isInterface()
: Retornatrue
se for uma interface.isEnum()
: Retornatrue
se for umenum
.isRecord()
: Retornatrue
se for umrecord
.isInstance(Object obj)
: Verifica se o objeto passado como parâmetro é uma instância deste tipo.isSealed()
: Retornatrue
se a classe ou interface forsealed
.
Para obter informações de hierarquia e pacote:
Class<?> getSuperclass()
: Retorna o objetoClass
da superclasse.String getPackageName()
: Retorna o nome do pacote onde o tipo está localizado.
Como Obter um Objeto Class
Para começar a usar a Reflection, o primeiro passo é obter o objeto Class
que representa o tipo de interesse. Existem três maneiras principais de fazer isso.
1. O Método getClass()
A forma mais comum de obter o objeto Class
é chamar o método getClass()
, que está disponível em todos os objetos (pois é herdado da classe Object
).
public class Program {
public static void main(String[] args) {
Person tom = new Person();
Class<?> personClass = tom.getClass();
System.out.println(personClass); // class Person
}
}
class Person { }
Se o tipo estiver em um pacote, a saída incluirá o nome totalmente qualificado.
public class Program {
public static void main(String[] args) {
String text = "hello";
System.out.println(text.getClass()); // class java.lang.String
}
}
2. O Método Estático Class.forName()
O método estático Class.forName()
permite obter um objeto Class
a partir de uma String
contendo o nome totalmente qualificado do tipo.
public class Program {
public static void main(String[] args) {
try {
Class<?> personClass = Class.forName("Person");
System.out.println(personClass); // class Person
Class<?> strClass = Class.forName("java.lang.String");
System.out.println(strClass); // class java.lang.String
} catch (ClassNotFoundException ex) {
System.out.println("Classe não encontrada.");
}
}
}
class Person { }
Este método é útil quando o nome da classe a ser carregada não é conhecido em tempo de compilação. É fundamental tratar a exceção ClassNotFoundException
, que é lançada se a JVM não conseguir encontrar a classe com o nome especificado.
3. O Literal de Classe .class
A terceira forma é usar o literal de classe (.class
), que pode ser anexado diretamente ao nome de qualquer tipo (classe, interface, enum ou tipo primitivo).
public class Program {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
System.out.println(personClass); // class Person
Class<String> strClass = String.class;
System.out.println(strClass); // class java.lang.String
}
}
class Person { }
Esta abordagem é a mais simples e segura quando o tipo é conhecido em tempo de compilação, pois a verificação é feita pelo compilador, não havendo risco de ClassNotFoundException
.
Resumo
- Reflection (Reflexão): É a capacidade de um programa Java inspecionar e manipular a si mesmo em tempo de execução.
- Classe
Class
: É o ponto de entrada da API de Reflection e armazena os metadados de todos os tipos em Java. Obtenção do Objeto
Class
: Existem três formas principais para obter um objetoClass
:objeto.getClass()
: A partir de uma instância de um objeto.Class.forName("nome.completo.da.Classe")
: A partir de uma string com o nome qualificado do tipo.NomeDoTipo.class
: Usando o literal de classe quando o tipo é conhecido em tempo de compilação.