Java Reflection: um exemplo prático

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.

 

1 Reply to “Java Reflection: um exemplo prático”

Leave a Reply

Your email address will not be published. Required fields are marked *