Reflexão (reflection) é uma característica que permite a um programa observar e até modificar a sua estrutura e o seu comportamento interno.
Os termos comumente utilizados são reflexão e introspecção. Na reflexão, um programa observa e modifica seu comportamento enquanto que na introspecção ele apenas observa e obtém informações dele mesmo.
A linguagem Java possui uma API de reflection que, na verdade, possui muito mais características de introspecção do que reflexão. Essa API possibilita o acesso a propriedades de classes, métodos e informações de anotações em tempo de execução. Esse mecanismo permite a um programa Java modificar o seu comportamento de acordo com as informações contidas na classe, tornando o programa muito mais flexível.
A princípio os conceitos de reflection parecem muito complexos, abstratos e sem perspectiva para aplicação prática, mas esses recursos são muito utilizados no desenvolvimento de frameworks e podem trazer também muitos benefícios no desenvolvimento de aplicações enterprise.
A seguir é apresentado um exemplo onde a reflection é utilizada para obter todas as propriedades de um objeto que podem ser acessadas por métodos get.
As classes a seguir são dois pojos, Product e Person, que terão suas propriedades expostas por meio de métodos get recuperadas por meio de reflection.
public class Person {
private long id;
private String name;
private String lastName;
private int age;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
public class Product {
private long id;
private String description;
private Double price;
private int quantity;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
A classe a seguir, ReflectionMapper, possui o método getAttributesMap que recebe como parâmetro um Object e retorna todas as propriedade do objeto que possam ser acessadas por um método get. O método obtém uma lista com todos os Method do objeto passado como parâmetro e, para cada método, verifica se o mesmo possui as características de um método get, se tiver, invoca-o e coloca o retorno em um Map para retorno.
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class ReflectionMapper {
/**
* @param obj
* @return Map containing the attributes' names and it's values
*/
public static Map<String, Object> getAttributesMap(Object obj) {
Map<String, Object> attributesMap = new HashMap<String, Object>();
Class<?> objClass = obj.getClass();
Method[] methods = objClass.getMethods();
for(Method method : methods) {
if(method.getName().startsWith("get") && method.getReturnType() != void.class) {
String attributeName = getAttributeName(method.getName());
try {
Object value = method.invoke(obj);
attributesMap.put(attributeName, value);
} catch (Exception e) {
e.printStackTrace();
}
}
}
return attributesMap;
}
private static String getAttributeName(String name) {
return name.substring(3);
}
}
A classe ReflectionTest utiliza o método getAttributesMap de ReflectionMapper. Note como esse código é elegante, permitindo o acesso a todas as propriedades de um objeto em poucas linhas de código. Independente do número de propriedades da classe, o acesso será feito com o mesmo número de linhas de código, sem a ncessidade de inúmeras chamadas a métodos get diretamente no objeto.
import java.util.Map;
public class ReflectionTest {
public static void main(String[] args) {
Person person = new Person();
person.setId(1);
person.setName("Gabriel");
person.setLastName("Amorim");
person.setAge(25);
Product product = new Product();
product.setId(1);
product.setDescription("Oxford Dictionary");
product.setPrice(11.90);
product.setQuantity(1);
Map<String, Object> attributes = ReflectionMapper.getAttributesMap(person);
for(String key : attributes.keySet()) {
System.out.println(key + ": " + attributes.get(key));
}
attributes = ReflectionMapper.getAttributesMap(product);
for(String key : attributes.keySet()) {
System.out.println(key + ": " + attributes.get(key));
}
}
}
Os exemplos apresentados aqui mostram o funcionamento da Reflection API de Java e como técnicas de programação voltadas a introspecção e reflexão podem transformar código repetitivo em códigos reutilizável. Há muito mais sobre a Reflection API para explorar, consulte também o tutorial oficial da Oracle sobre Reflection API.
Excelente explicação! Simples mas completa.