1. Criação de objetos com reflexão
Às vezes precisamos criar objetos sem conhecer seu tipo em tempo de compilação. Por exemplo, o nome da classe vem de uma configuração, ou estamos escrevendo um framework genérico (serializador, contêiner de DI). Em código comum, escreveríamos:
User user = new User("Ivan", 25);
Mas e se não soubermos que a classe se chama User e não conhecermos os parâmetros do seu construtor? É aí que a reflexão ajuda.
Abordagem obsoleta: Class.newInstance()
Antigamente, o Java tinha o método Class.newInstance(), que cria um objeto por meio de um construtor público sem parâmetros:
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.newInstance();
IMPORTANTE: Esse método foi marcado como deprecated desde o Java 9 e não é recomendado. Ele não permite escolher o construtor, oculta as causas dos erros e exige apenas um public-construtor sem parâmetros.
Abordagem moderna: via construtores
No código real, nem sempre existe um public-construtor sem parâmetros. A abordagem atual é obter o construtor necessário via getConstructor(...) ou getDeclaredConstructor(...) e então chamar newInstance(...) nele.
Exemplo: criar um objeto com parâmetros
public class User {
private String name;
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
import java.lang.reflect.Constructor;
Class<?> clazz = Class.forName("User");
// Obtemos o construtor com parâmetros String, int
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
// Criamos o objeto, passamos os parâmetros
Object user = constructor.newInstance("Ivan", 25);
System.out.println(user); // toString, se definido
O que está acontecendo aqui?
- Obtemos o construtor necessário pela assinatura.
- Chamamos newInstance(...) nele, passando os argumentos.
- Recebemos um objeto do tipo Object (pode ser convertido para User, se o tipo for conhecido).
E se o construtor for privado?
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
constructor.setAccessible(true); // Mágica! Agora é possível chamar o construtor privado.
Object user = constructor.newInstance("Ivan", 25);
ATENÇÃO: Evite o uso excessivo disso — quebra a encapsulação. Em aplicativos modulares (Java 9+), também existem restrições de acesso.
Tabela comparativa das formas de criar objetos
| Forma | Velocidade | Segurança de tipos | Quando usar |
|---|---|---|---|
|
A mais rápida | Total | Sempre que possível |
|
Lenta | Perdida | Frameworks, plugins |
|
Média | Parcial | Bibliotecas de alto desempenho |
| Fábricas | Rápida | Total | Criação flexível de objetos |
2. Invocação de métodos via reflexão
Você pode invocar qualquer método de um objeto, mesmo sem conhecer seu nome em tempo de compilação, ou se ele for private.
Obtendo um método
import java.lang.reflect.Method;
Class<?> clazz = user.getClass();
// Obtemos um método public pelo nome e tipos de parâmetro
Method method = clazz.getMethod("getName"); // Sem parâmetros
Object result = method.invoke(user); // Chamada de método sem parâmetros
System.out.println(result); // Irá imprimir o nome do usuário
Invocando um método com parâmetros
Method setName = clazz.getMethod("setName", String.class);
setName.invoke(user, "Petr"); // Define um novo nome
Invocando um método privado
Method secret = clazz.getDeclaredMethod("secretMethod", int.class);
secret.setAccessible(true); // Remove a restrição
Object secretResult = secret.invoke(user, 123);
Importante: Todos os parâmetros são passados como um array de objetos (varargs). Se o método retornar algo — o resultado virá como Object.
3. Acesso a campos via reflexão
Às vezes é necessário ler ou alterar o valor de um campo, mesmo que ele seja private (por exemplo, em serialização ou testes).
Obtendo um campo
import java.lang.reflect.Field;
Class<?> clazz = user.getClass();
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // Se o campo não for public
// Leitura do valor
Object age = ageField.get(user);
System.out.println("Idade: " + age);
// Alteração do valor
ageField.set(user, 42);
System.out.println("Nova idade: " + ageField.get(user));
Trabalhando com campos estáticos
Se o campo for estático, passe null para get/set em vez do objeto:
Field staticField = clazz.getDeclaredField("counter");
staticField.setAccessible(true);
staticField.set(null, 100); // Para campos static, não é necessário objeto
4. Restrições e exceções
O trabalho com reflexão pode gerar muitas checked‑exceptions. As mais comuns:
- ClassNotFoundException — a classe com esse nome não foi encontrada.
- NoSuchMethodException — não há construtor ou método com essa assinatura.
- NoSuchFieldException — campo não encontrado.
- IllegalAccessException — sem acesso ao membro (por exemplo, método privado sem setAccessible(true)).
- InstantiationException — a classe é abstrata ou é uma interface; não é possível criar o objeto.
- InvocationTargetException — erro dentro do construtor ou método invocado.
Exemplo de tratamento:
try {
// ... seu código com reflexão ...
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
Se os detalhes não importarem, é mais conveniente capturar a superclasse comum ReflectiveOperationException (Java 7+).
5. Prática: miniaplicativo “Instanciador Dinâmico”
Exemplo de classe para experimentos
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private void sayHello() {
System.out.println("Olá, meu nome é " + name + ", tenho " + age + " anos.");
}
}
Criação e manipulação dinâmicas
import java.lang.reflect.*;
public class ReflectionDemo {
public static void main(String[] args) {
try {
// 1. Obtemos o objeto Class pelo nome
Class<?> clazz = Class.forName("Person");
// 2. Obtemos o construtor e criamos o objeto
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object person = constructor.newInstance("Alice", 30);
// 3. Invocamos o método privado sayHello
Method sayHello = clazz.getDeclaredMethod("sayHello");
sayHello.setAccessible(true);
sayHello.invoke(person); // Olá, meu nome é Alice, tenho 30 anos.
// 4. Alteramos o campo privado name
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
nameField.set(person, "Bob");
// 5. Invocamos sayHello novamente
sayHello.invoke(person); // Olá, meu nome é Bob, tenho 30 anos.
} catch (ReflectiveOperationException e) {
e.printStackTrace();
}
}
}
Observe:
- Tudo é feito sem conhecimento direto do tipo Person no código.
- É possível alterar campos privados e invocar métodos privados (se isso for permitido pela política de segurança da JVM e pela configuração de módulos).
6. Como isso se relaciona com aplicações reais?
A reflexão está na base de muitas bibliotecas e frameworks populares:
- JUnit: busca de métodos com a anotação @Test, criação de instâncias de testes e invocação de métodos.
- Spring: DI, criação de beans, autowiring.
- Jackson, Gson: serialização/desserialização de campos de objetos.
- Hibernate: acesso a campos de entidades, proxies e carregamento lazy.
7. Esquema visual: como um objeto é criado via reflexão
flowchart TB
A["Nome da classe (String)"] --> B["Class.forName"]
B --> C["Class<?>"]
C --> D["getConstructor(...)"]
D --> E["Constructor<?>"]
E --> F["newInstance(...)"]
F --> G["Object"]
8. Erros comuns ao trabalhar com reflexão
Erro nº 1: construtor errado. Solicitar um construtor com assinatura incorreta, por exemplo, getConstructor(String.class) quando há apenas um construtor com dois parâmetros — você receberá NoSuchMethodException. Sempre verifique as assinaturas!
Erro nº 2: falta de acesso. Invocar um construtor/método privado sem setAccessible(true) leva a IllegalAccessException. E lembre-se: no Java 9+, o sistema de módulos pode impor restrições adicionais.
Erro nº 3: problemas de tipos. Todos os parâmetros e valores de retorno via reflexão — são Object. Um cast incorreto resulta em ClassCastException.
Erro nº 4: exceções não tratadas. A reflexão lança muitas checked‑exceptions. Trate-as, por exemplo, com a superclasse ReflectiveOperationException; caso contrário, o código não irá compilar.
Erro nº 5: violação de encapsulamento. Uso indiscriminado de setAccessible(true) — “acesso à geladeira alheia”. Use apenas quando realmente necessário e leve em conta os requisitos de segurança.
GO TO FULL VERSION