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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
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.